Skip to content

Commit

Permalink
Merge pull request #29 from hiddevdploeg/update-design-and-example
Browse files Browse the repository at this point in the history
Update design and example
  • Loading branch information
hiddevdploeg authored Oct 29, 2023
2 parents 7329159 + 1c85b37 commit c6c74e4
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Example/BillboardExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct ContentView: View {
List {
if let advert = allAds.randomElement() {
Section {
BillboardBannerView(advert: advert)
BillboardBannerView(advert: advert, hideDismissButtonAndTimer: true)
.listRowBackground(Color.clear)
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
}
Expand Down
62 changes: 52 additions & 10 deletions Sources/Billboard/BillboardViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import OSLog

public final class BillboardViewModel : ObservableObject {

Expand Down Expand Up @@ -35,6 +36,42 @@ public final class BillboardViewModel : ObservableObject {
}
}

public static func fetchRandomAd(excludedIDs: [String] = []) async throws -> BillboardAd? {
guard let url = BillboardConfiguration().adsJSONURL else {
return nil
}

let session = URLSession(configuration: BillboardViewModel.networkConfiguration)
session.sessionDescription = "Fetching Billboard Ad"

do {
let (data, _) = try await session.data(from: url)
let decoder = JSONDecoder()
let response = try decoder.decode(BillboardAdResponse.self, from: data)
let filteredAds = response.ads.filter({ !excludedIDs.contains($0.appStoreID) })
let adToShow = filteredAds.randomElement()

if let adToShow {
Logger.billboard.debug("✨ Billboard Ad presented: \(adToShow.name)")
}

return adToShow

} catch DecodingError.keyNotFound(let key, let context) {
Logger.billboard.error("❌ Failed to decode Billboard Ad due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
} catch DecodingError.typeMismatch(_, let context) {
Logger.billboard.error("❌ Failed to decode Billboard Ad due to type mismatch – \(context.debugDescription)")
} catch DecodingError.valueNotFound(let type, let context) {
Logger.billboard.error("❌ Failed to decode Billboard Ad due to missing \(type) value – \(context.debugDescription)")
} catch DecodingError.dataCorrupted(_) {
Logger.billboard.error("❌ Failed to decode Billboard Ad because it appears to be invalid JSON")
} catch {
Logger.billboard.error("❌ Failed to decode Billboard Ad: \(error.localizedDescription)")
}

return nil
}

public static func fetchRandomAd(from url: URL, excludedIDs: [String] = []) async throws -> BillboardAd? {
let session = URLSession(configuration: BillboardViewModel.networkConfiguration)
session.sessionDescription = "Fetching Billboard Ad"
Expand All @@ -45,18 +82,23 @@ public final class BillboardViewModel : ObservableObject {
let response = try decoder.decode(BillboardAdResponse.self, from: data)
let filteredAds = response.ads.filter({ !excludedIDs.contains($0.appStoreID) })
let adToShow = filteredAds.randomElement()

if let adToShow {
Logger.billboard.debug("✨ Billboard Ad presented: \(adToShow.name)")
}

return adToShow

} catch DecodingError.keyNotFound(let key, let context) {
print("❌ Failed to decode due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
} catch DecodingError.typeMismatch(_, let context) {
print("❌ Failed to decode from bundle due to type mismatch – \(context.debugDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad due to type mismatch – \(context.debugDescription)")
} catch DecodingError.valueNotFound(let type, let context) {
print("❌ Failed to decode from bundle due to missing \(type) value – \(context.debugDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad due to missing \(type) value – \(context.debugDescription)")
} catch DecodingError.dataCorrupted(_) {
print("❌ Failed to decode from bundle because it appears to be invalid JSON")
Logger.billboard.error("❌ Failed to decode Billboard Ad because it appears to be invalid JSON")
} catch {
print("❌ Failed to decode from bundle: \(error.localizedDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad: \(error.localizedDescription)")
}

return nil
Expand All @@ -73,15 +115,15 @@ public final class BillboardViewModel : ObservableObject {
return response.ads

} catch DecodingError.keyNotFound(let key, let context) {
print("❌ Failed to decode due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad due to missing key '\(key.stringValue)' not found – \(context.debugDescription)")
} catch DecodingError.typeMismatch(_, let context) {
print("❌ Failed to decode from bundle due to type mismatch – \(context.debugDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad due to type mismatch – \(context.debugDescription)")
} catch DecodingError.valueNotFound(let type, let context) {
print("❌ Failed to decode from bundle due to missing \(type) value – \(context.debugDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad due to missing \(type) value – \(context.debugDescription)")
} catch DecodingError.dataCorrupted(_) {
print("❌ Failed to decode from bundle because it appears to be invalid JSON")
Logger.billboard.error("❌ Failed to decode Billboard Ad because it appears to be invalid JSON")
} catch {
print("❌ Failed to decode from bundle: \(error.localizedDescription)")
Logger.billboard.error("❌ Failed to decode Billboard Ad: \(error.localizedDescription)")
}

return []
Expand Down
16 changes: 16 additions & 0 deletions Sources/Billboard/Utilities/Logger+Ext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Logger+Ext.swift
//
//
// Created by Hidde van der Ploeg on 29/10/2023.
//

import OSLog

extension Logger {
/// Using your bundle identifier is a great way to ensure a unique identifier.
private static var subsystem = Bundle.main.bundleIdentifier!

/// Logs coming from the Billboard SPM
static let billboard = Logger(subsystem: subsystem, category: "Billboard")
}
17 changes: 11 additions & 6 deletions Sources/Billboard/Views/BillboardBannerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public struct BillboardBannerView : View {
Button {
if let url = advert.appStoreLink {
openURL(url)
canDismiss = true
}
canDismiss = true
} label: {
HStack(spacing: 10) {
if let appIcon {
Expand Down Expand Up @@ -120,12 +120,17 @@ public struct BillboardBannerView : View {
}
}

@ViewBuilder
var backgroundView : some View {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(advert.background)
.shadow(color: includeShadow ? advert.background.opacity(0.5) : Color.clear,
radius: 6,
x: 0, y: 2)
if #available(iOS 16.0, *) {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(advert.background.gradient)
.shadow(color: includeShadow ? advert.background.opacity(0.5) : Color.clear, radius: 6, x: 0, y: 2)
} else {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(advert.background)
.shadow(color: includeShadow ? advert.background.opacity(0.5) : Color.clear, radius: 6, x: 0, y: 2)
}
}
}

Expand Down
36 changes: 25 additions & 11 deletions Sources/Billboard/Views/BillboardCountdownView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct BillboardCountdownView : View {
@Binding var canDismiss : Bool


@State private var seconds : Int = 15
@State private var seconds : Double = 15
@State private var timerProgress : CGFloat = 0.0

private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
Expand All @@ -26,22 +26,36 @@ struct BillboardCountdownView : View {
Circle()
.trim(from: 0, to: timerProgress)
.stroke(advert.tint, style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round))

Text("\(seconds)")
.font(.compatibleSystem(.caption, design: .rounded, weight: .bold)).monospacedDigit()
.rotationEffect(.degrees(90))
.minimumScaleFactor(0.5)
.onReceive(timer) { _ in
if seconds > 0 {
seconds -= 1
if #available(iOS 17.0, *) {
Text("\(seconds, specifier: "%.0f")")
.font(.compatibleSystem(.caption, design: .rounded, weight: .heavy)).monospacedDigit()
.rotationEffect(.degrees(90))
.minimumScaleFactor(0.5)
.animation(.default, value: seconds)
.transition(.identity)
.contentTransition(.numericText(value: seconds))
.onReceive(timer) { _ in
if seconds > 0 {
seconds -= 1
}
}
} else {
Text("\(seconds, specifier: "%.0f")")
.font(.compatibleSystem(.caption, design: .rounded, weight: .bold)).monospacedDigit()
.rotationEffect(.degrees(90))
.minimumScaleFactor(0.5)
.onReceive(timer) { _ in
if seconds > 0 {
seconds -= 1
}
}
}
}
}
.foregroundColor(advert.tint)
.rotationEffect(.degrees(-90))
.frame(width: 32, height: 32)
.onAppear {
seconds = Int(totalDuration)
seconds = totalDuration
withAnimation(.linear(duration: totalDuration)) {
timerProgress = 1.0
}
Expand Down

0 comments on commit c6c74e4

Please sign in to comment.