Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ public enum AppGroupConfig {
public static var suffix: String = ".optimove"
}

enum AppGroupsHelper {
static func isKumulosAppGroupDefined() -> Bool {
public enum AppGroupsHelper {
public static func isKumulosAppGroupDefined() -> Bool {
let containerUrl = getSharedContainerPath()

return containerUrl != nil
}

static func getSharedContainerPath() -> URL? {
public static func getSharedContainerPath() -> URL? {
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: getKumulosGroupName())
}

Expand Down
13 changes: 13 additions & 0 deletions OptimobileCore/Sources/Credentials.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright © 2023 Optimove. All rights reserved.

import Foundation

public struct OptimobileCredentials: Codable {
public let apiKey: String
public let secretKey: String

public init(apiKey: String, secretKey: String) {
self.apiKey = apiKey
self.secretKey = secretKey
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

import Foundation

extension Notification.Name {
public extension Notification.Name {
static let optimobileInializationFinished = Notification.Name("optimobileInializationFinished")
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ protocol KeyValPersistent {
static func removeObject(forKey: String)
}

enum KeyValPersistenceHelper {
static func maybeMigrateUserDefaultsToAppGroups() {
public enum KeyValPersistenceHelper {
public static func maybeMigrateUserDefaultsToAppGroups() {
let standardDefaults = UserDefaults.standard
let haveMigratedKey: String = OptimobileUserDefaultsKey.MIGRATED_TO_GROUPS.rawValue
if !AppGroupsHelper.isKumulosAppGroupDefined() {
Expand Down Expand Up @@ -45,15 +45,15 @@ enum KeyValPersistenceHelper {
}

extension KeyValPersistenceHelper: KeyValPersistent {
static func set(_ value: Any?, forKey: String) {
public static func set(_ value: Any?, forKey: String) {
getUserDefaults().set(value, forKey: forKey)
}

static func object(forKey: String) -> Any? {
public static func object(forKey: String) -> Any? {
return getUserDefaults().object(forKey: forKey)
}

static func removeObject(forKey: String) {
public static func removeObject(forKey: String) {
getUserDefaults().removeObject(forKey: forKey)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import Foundation

enum MediaHelper {
public enum MediaHelper {
enum Error: LocalizedError {
case noMediaUrlFound
case invalidPictureUrl(String)
}

static func getCompletePictureUrl(pictureUrlString: String, width: UInt) throws -> URL {
public static func getCompletePictureUrl(pictureUrlString: String, width: UInt) throws -> URL {
if pictureUrlString.hasPrefix("https://") || pictureUrlString.hasPrefix("http://") {
guard let url = URL(string: pictureUrlString) else {
throw Error.invalidPictureUrl(pictureUrlString)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright © 2023 Optimove. All rights reserved.

enum OptimobileEvent: String, Codable {
public enum OptimobileEvent: String, Codable {
case DEEP_LINK_MATCHED = "k.deepLink.matched"
case DEVICE_UNSUBSCRIBED = "k.push.deviceUnsubscribed"
case ENGAGE_BEACON_ENTERED_PROXIMITY = "k.engage.beaconEnteredProximity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import Foundation

let KS_MESSAGE_TYPE_PUSH = 1
public let KS_MESSAGE_TYPE_PUSH = 1

enum OptimobileHelper {
public enum OptimobileHelper {
private static let installIdLock = DispatchSemaphore(value: 1)
static let userIdLock = DispatchSemaphore(value: 1)
public static let userIdLock = DispatchSemaphore(value: 1)

static var installId: String {
public static var installId: String {
installIdLock.wait()
defer {
installIdLock.signal()
Expand All @@ -29,7 +29,7 @@ enum OptimobileHelper {

If no user is associated, it returns the Kumulos installation ID
*/
static var currentUserIdentifier: String {
public static var currentUserIdentifier: String {
userIdLock.wait()
defer { userIdLock.signal() }
if let userId = KeyValPersistenceHelper.object(forKey: OptimobileUserDefaultsKey.USER_ID.rawValue) as! String? {
Expand All @@ -39,7 +39,7 @@ enum OptimobileHelper {
return OptimobileHelper.installId
}

static func getBadgeFromUserInfo(userInfo: [AnyHashable: Any]) -> NSNumber? {
public static func getBadgeFromUserInfo(userInfo: [AnyHashable: Any]) -> NSNumber? {
let custom = userInfo["custom"] as? [AnyHashable: Any]
let aps = userInfo["aps"] as? [AnyHashable: Any]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import Foundation

enum OptimobileUserDefaultsKey: String {
public enum OptimobileUserDefaultsKey: String {
case REGION = "KumulosEventsRegion"
case MEDIA_BASE_URL = "KumulosMediaBaseUrl"
case INSTALL_UUID = "KumulosUUID"
Expand Down
15 changes: 15 additions & 0 deletions OptimobileCore/Sources/PendingNotification.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright © 2022 Optimove. All rights reserved.

import Foundation

public struct PendingNotification: Codable {
public let id: Int
public let deliveredAt: Date
public let identifier: String

public init(id: Int, deliveredAt: Date = .init(), identifier: String) {
self.id = id
self.deliveredAt = deliveredAt
self.identifier = identifier
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import Foundation

enum PendingNotificationHelper {
static func remove(id: Int) {
public enum PendingNotificationHelper {
public static func remove(id: Int) {
var pendingNotifications = readAll()

if let i = pendingNotifications.firstIndex(where: { $0.id == id }) {
Expand All @@ -13,7 +13,7 @@ enum PendingNotificationHelper {
}
}

static func remove(identifier: String) {
public static func remove(identifier: String) {
var pendingNotifications = readAll()

if let i = pendingNotifications.firstIndex(where: { $0.identifier == identifier }) {
Expand All @@ -23,7 +23,7 @@ enum PendingNotificationHelper {
}
}

static func readAll() -> [PendingNotification] {
public static func readAll() -> [PendingNotification] {
var pendingNotifications = [PendingNotification]()
if let data = KeyValPersistenceHelper.object(forKey: OptimobileUserDefaultsKey.PENDING_NOTIFICATIONS.rawValue),
let decoded = try? JSONDecoder().decode([PendingNotification].self, from: data as! Data)
Expand All @@ -34,7 +34,7 @@ enum PendingNotificationHelper {
return pendingNotifications
}

static func add(notification: PendingNotification) {
public static func add(notification: PendingNotification) {
var pendingNotifications = readAll()

if let _ = pendingNotifications.firstIndex(where: { $0.id == notification.id }) {
Expand All @@ -46,7 +46,7 @@ enum PendingNotificationHelper {
save(pendingNotifications: pendingNotifications)
}

fileprivate static func save(pendingNotifications: [PendingNotification]) {
static func save(pendingNotifications: [PendingNotification]) {
if let data = try? JSONEncoder().encode(pendingNotifications) {
KeyValPersistenceHelper.set(data, forKey: OptimobileUserDefaultsKey.PENDING_NOTIFICATIONS.rawValue)
}
Expand Down
117 changes: 117 additions & 0 deletions OptimobileCore/Sources/PushNotification.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright © 2023 Optimove. All rights reserved.

import Foundation

/// Represents a push notification received from the server.
public struct PushNotification: Decodable {
public struct Aps: Decodable {
public struct Alert: Decodable {
public let title: String?
public let body: String?
}

public let alert: Alert?
public let badge: Int?
public let sound: String?
/// The background notification flag. To perform a silent background update, specify the value 1 and don’t include the alert, badge, or sound keys in your payload. If this key is present with a value of 1, the system attempts to initialize your app in the background so that it can make updates to its user interface. If the app is already running in the foreground, this key has no effect.
public let isBackground: Bool
/// The notification service app extension flag. If the value is 1, the system passes the notification to your notification service app extension before delivery. Use your extension to modify the notification’s content.
public let isExtension: Bool

private enum CodingKeys: String, CodingKey {
case alert
case badge
case sound
case isBackground = "content-available"
case isExtension = "mutable-content"
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.alert = try container.decodeIfPresent(Alert.self, forKey: .alert)
self.badge = try container.decodeIfPresent(Int.self, forKey: .badge)
self.sound = try container.decodeIfPresent(String.self, forKey: .sound)
let isBackground = try container.decodeIfPresent(Int.self, forKey: .isBackground)
self.isBackground = isBackground == 1
let isExtension = try container.decodeIfPresent(Int.self, forKey: .isExtension)
self.isExtension = isExtension == 1
}
}

public struct Attachment: Decodable {
public let pictureUrl: String?
}

public struct Button: Decodable {
public struct Icon: Decodable {
public enum IconType: String, Decodable {
case custom
case system
}

public let id: String
public let type: IconType
}

public let id: String
public let icon: Icon?
public let text: String
}

public struct Data: Decodable {
public let id: Int

private enum CodingKeys: String, CodingKey {
case id
case data
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let data = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .data)
self.id = try data.decode(Int.self, forKey: CodingKeys.id)
}
}

public let aps: Aps
public let attachment: PushNotification.Attachment?
/// Optimove badge
public let badge: Int?
public let buttons: [PushNotification.Button]?
public let deeplink: PushNotification.Data?
public let message: PushNotification.Data
public let url: URL?

private enum CodingKeys: String, CodingKey {
case a
case aps
case attachments
case badge = "badge_inc"
case buttons = "k.buttons"
case custom
case deeplink = "k.deepLink"
case message = "k.message"
case u
}

public init(userInfo: [AnyHashable: Any]) throws {
let data = try JSONSerialization.data(withJSONObject: userInfo)
let decoder = JSONDecoder()
self = try decoder.decode(PushNotification.self, from: data)
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.aps = try container.decode(Aps.self, forKey: .aps)
self.attachment = try container.decodeIfPresent(Attachment.self, forKey: .attachments)

let custom = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .custom)
self.badge = try custom.decodeIfPresent(Int.self, forKey: .badge)
self.url = try custom.decodeIfPresent(URL.self, forKey: .u)

let a = try custom.nestedContainer(keyedBy: CodingKeys.self, forKey: .a)
self.buttons = try a.decodeIfPresent([Button].self, forKey: .buttons)
self.deeplink = try a.decodeIfPresent(PushNotification.Data.self, forKey: .deeplink)
self.message = try a.decode(PushNotification.Data.self, forKey: .message)
}
}
20 changes: 20 additions & 0 deletions OptimobileCore/Tests/MediaHelperTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright © 2023 Optimove. All rights reserved.

@testable import OptimobileCore
import XCTest

final class MediaHelperTests: XCTestCase {
func test_getCompletePictureUrl() throws {
let pictureUrlString = "https://www.optimove.com/wp-content/uploads/2018/12/optimove-logo.png"
let url = try MediaHelper.getCompletePictureUrl(pictureUrlString: pictureUrlString, width: 100)
XCTAssertEqual(url.absoluteString, pictureUrlString)
}

func test_getCompletePictureUrl_withMediaUrl() throws {
let pictureUrlString = "B04wM4Y7/b2f69e254879d69b58c7418468213762.jpeg"
let mediaUrl = "https://www.optimove.com"
KeyValPersistenceHelper.set(mediaUrl, forKey: OptimobileUserDefaultsKey.MEDIA_BASE_URL.rawValue)
let url = try MediaHelper.getCompletePictureUrl(pictureUrlString: pictureUrlString, width: 100)
XCTAssertEqual(url.absoluteString, "\(mediaUrl)/100x/\(pictureUrlString)")
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright © 2023 Optimove. All rights reserved.

@testable import OptimoveNotificationServiceExtension
import OptimobileCore
import OptimoveTest
import XCTest

final class PushNotificationTests: XCTestCase, FileAccessible {
var fileName: String = ""

func test_decode_id() throws {
func test_decode_message() throws {
fileName = "notification-message.json"
let decoder = JSONDecoder()
let notification = try decoder.decode(PushNotification.self, from: data)
XCTAssertEqual(notification.id, 1)
XCTAssertEqual(notification.message.id, 1)
}

func test_decode_badge() throws {
Expand Down Expand Up @@ -46,14 +46,30 @@ final class PushNotificationTests: XCTestCase, FileAccessible {
let decoder = JSONDecoder()
let notification = try decoder.decode(PushNotification.self, from: data)

XCTAssertEqual(notification.picturePath, "B04wM4Y7/b2f69e254879d69b58c7418468213762.jpeg")
XCTAssertEqual(notification.attachment?.pictureUrl, "B04wM4Y7/b2f69e254879d69b58c7418468213762.jpeg")
}

func test_decode_background() throws {
fileName = "notification-background.json"
let decoder = JSONDecoder()
let notification = try decoder.decode(PushNotification.self, from: data)

XCTAssertEqual(notification.isBackground, true)
XCTAssertEqual(notification.aps.isBackground, true)
}

func test_decode_url() throws {
fileName = "notification-url.json"
let decoder = JSONDecoder()
let notification = try decoder.decode(PushNotification.self, from: data)

XCTAssertEqual(notification.url?.absoluteString, "https://www.optimove.com")
}

func test_decode_deeplink() throws {
fileName = "notification-deeplink.json"
let decoder = JSONDecoder()
let notification = try decoder.decode(PushNotification.self, from: data)

XCTAssertEqual(notification.deeplink?.id, 1)
}
}
Loading