Skip to content

Commit

Permalink
✨ Add support for post experience actions
Browse files Browse the repository at this point in the history
  • Loading branch information
mmaatttt committed Jul 21, 2022
1 parent 4107dd9 commit 4f01a4d
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 4 deletions.
25 changes: 23 additions & 2 deletions Sources/AppcuesKit/Data/Models/Experience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,34 @@ internal struct Experience {
let traits: [Trait]
let steps: [Step]

// Post experience actions
let redirectURL: URL?
let nextContentID: String?

/// Unique ID to disambiguate the same experience flowing through the system from different origins.
let instanceID = UUID()
}

extension Experience: Decodable {
private enum CodingKeys: CodingKey {
case id, name, type, publishedAt, traits, steps
private enum CodingKeys: String, CodingKey {
case id, name, type, publishedAt, traits, steps, redirectURL = "redirectUrl", nextContentID = "nextContentId"
}
}

extension Experience {
@available(iOS 13.0, *)
var postExperienceActions: [ExperienceAction] {
var actions: [ExperienceAction] = []

if let redirectURL = redirectURL {
actions.append(AppcuesLinkAction(url: redirectURL))
}

if let nextContentID = nextContentID {
actions.append(AppcuesLaunchExperienceAction(experienceID: nextContentID))
}

return actions
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ internal class AppcuesLaunchExperienceAction: ExperienceAction {
}
}

init(experienceID: String) {
self.experienceID = experienceID
}

func execute(inContext appcues: Appcues, completion: @escaping ActionRegistry.Completion) {
appcues.show(experienceID: experienceID) { _, _ in completion() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ internal class AppcuesLinkAction: ExperienceAction {
}
}

init(url: URL, openExternally: Bool = false) {
self.url = url
self.openExternally = openExternally
}

func execute(inContext appcues: Appcues, completion: @escaping ActionRegistry.Completion) {
// SFSafariViewController only supports HTTP and HTTPS URLs and crashes otherwise, so check to be safe.
if !openExternally && ["http", "https"].contains(url.scheme?.lowercased()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ extension ExperienceStateMachine {
)
case let (.endingStep(experience, currentIndex, _), .startStep(stepRef)):
return Transition.fromEndingStepToBeginningStep(experience, currentIndex, stepRef, traitComposer)
case (.endingExperience, .reset):
return Transition(toState: .idling)
case let (.endingExperience(experience, stepIndex, markComplete), .reset):
var sideEffect: SideEffect?
if markComplete || stepIndex == experience.stepIndices.last {
sideEffect = .processActions(experience.postExperienceActions)
}
return Transition(toState: .idling, sideEffect: sideEffect)

// Error cases
case let (_, .startExperience(experience)):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal class ExperienceStateMachine {

private let config: Appcues.Config
private let traitComposer: TraitComposing
private let actionRegistry: ActionRegistry

private(set) var stateObservers: [ExperienceStateObserver] = []

Expand All @@ -33,6 +34,7 @@ internal class ExperienceStateMachine {
init(container: DIContainer, initialState: State = .idling) {
config = container.resolve(Appcues.Config.self)
traitComposer = container.resolve(TraitComposing.self)
actionRegistry = container.resolve(ActionRegistry.self)

state = initialState
}
Expand Down Expand Up @@ -214,6 +216,7 @@ extension ExperienceStateMachine {
case navigateInContainer(ExperiencePackage, pageIndex: Int)
case dismissContainer(ExperiencePackage, continuation: Action)
case error(ExperienceError, reset: Bool)
case processActions([ExperienceAction])

func execute(in machine: ExperienceStateMachine) throws {
switch self {
Expand All @@ -233,6 +236,8 @@ extension ExperienceStateMachine {
if reset {
machine.state = .idling
}
case let .processActions(actions):
machine.actionRegistry.enqueue(actionInstances: actions)
}
}

Expand Down

0 comments on commit 4f01a4d

Please sign in to comment.