-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Match Game Content Notification Extension (#28)
* renamed to push story content extension * added match game * removed matchgameview, series of matchcardviews * game over * added play again button * cleaned up * bigger font * added score apparatus * match game * cleaned up * supporting portrait/landscape mode * folders * updated ui * 374 * passing data from app group to app * using number of cards * using xib, not storyboard * removed files * cleaned up * privatized * added comments * userDefaults * cleaned up * changed filename * no more spaces * set constraint to 999 to resolve layout conflicts on collapse
- Loading branch information
Justin Malandruccolo
authored
Feb 23, 2021
1 parent
08f4f66
commit 7a9ed52
Showing
138 changed files
with
1,055 additions
and
78 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
146 changes: 146 additions & 0 deletions
146
Braze-Demo-Match-Game-Content-Extension/Base.lproj/MainInterface.storyboard
Large diffs are not rendered by default.
Oops, something went wrong.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>$(DEVELOPMENT_LANGUAGE)</string> | ||
<key>CFBundleDisplayName</key> | ||
<string>Braze Demo Match Game Content Extension</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>$(MARKETING_VERSION)</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
<key>NSExtension</key> | ||
<dict> | ||
<key>NSExtensionAttributes</key> | ||
<dict> | ||
<key>UNNotificationExtensionCategory</key> | ||
<string>match_game</string> | ||
<key>UNNotificationExtensionDefaultContentHidden</key> | ||
<true/> | ||
<key>UNNotificationExtensionInitialContentSizeRatio</key> | ||
<real>0.65000000000000002</real> | ||
<key>UNNotificationExtensionUserInteractionEnabled</key> | ||
<true/> | ||
</dict> | ||
<key>NSExtensionMainStoryboard</key> | ||
<string>MainInterface</string> | ||
<key>NSExtensionPointIdentifier</key> | ||
<string>com.apple.usernotifications.content-extension</string> | ||
</dict> | ||
</dict> | ||
</plist> |
17 changes: 17 additions & 0 deletions
17
Braze-Demo-Match-Game-Content-Extension/Model/AttemptCounter.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
struct AttemptCounter { | ||
private(set) var attemptCount = 0 | ||
private var bestScore = Int.max | ||
|
||
mutating func increment() { | ||
attemptCount += 1 | ||
} | ||
|
||
mutating func getHighScore() -> Int { | ||
bestScore = min(bestScore, attemptCount) | ||
return bestScore | ||
} | ||
|
||
mutating func reset() { | ||
attemptCount = 0 | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
Braze-Demo-Match-Game-Content-Extension/Model/MatchCard.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import UIKit | ||
|
||
enum CardType: String, CaseIterable { | ||
case angry | ||
case heartEyes = "heart-eyes" | ||
case tearsOfJoy = "tears-of-joy" | ||
case rainbow | ||
case pensive | ||
case stunned | ||
} | ||
|
||
struct MatchCard { | ||
private(set) var type: CardType | ||
|
||
var selectedImage: UIImage? { | ||
return UIImage(named: type.rawValue) | ||
} | ||
} | ||
|
||
extension MatchCard: Equatable { | ||
static func ==(lhs: MatchCard, rhs: MatchCard) -> Bool { | ||
return lhs.type == rhs.type | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
Braze-Demo-Match-Game-Content-Extension/Model/MatchGame.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
protocol MatchGameDelegate: class { | ||
func cardsDidLoad(_ cards: [MatchCard]) | ||
func cardsDidMatch(_ indicies: [Int], currentScore: Int) | ||
func cardsDidNotMatch(_ indicies: [Int], currentScore: Int) | ||
} | ||
|
||
struct MatchGame { | ||
|
||
// MARK: - Variables | ||
private var cards = [MatchCard]() | ||
private var numberOfCards = 0 | ||
private var matchedCardsCount = 0 | ||
private var flippedIndicies = [Int]() // represents the 2 flipped cards | ||
private var attemptCounter = AttemptCounter() | ||
private weak var delegate: MatchGameDelegate? | ||
|
||
var noMatchesLeft: Bool { | ||
return matchedCardsCount == cards.count | ||
} | ||
} | ||
|
||
// MARK: - Public Methods | ||
extension MatchGame { | ||
mutating func configureGame(numberOfCards: Int, delegate: MatchGameDelegate? = nil) { | ||
self.numberOfCards = numberOfCards | ||
self.delegate = delegate | ||
|
||
loadCards(numberOfCards: numberOfCards) | ||
} | ||
|
||
/// The top-left card on the board has an index of 0 and increments from left to right. The bottom-right card has an index of n-1, n representing the number of cards. | ||
mutating func cardFlipped(at index: Int) { | ||
flippedIndicies.append(index) | ||
|
||
if flippedIndicies.count == 2 { | ||
checkForMatch(with: flippedIndicies) | ||
flippedIndicies.removeAll() | ||
} | ||
} | ||
|
||
mutating func getHighScore() -> Int { | ||
return attemptCounter.getHighScore() | ||
} | ||
|
||
mutating func playAgain() { | ||
matchedCardsCount = 0 | ||
attemptCounter.reset() | ||
|
||
loadCards(numberOfCards: numberOfCards) | ||
} | ||
} | ||
|
||
// MARK: - Private Methods | ||
private extension MatchGame { | ||
/// Creates the deck of cards to be synced up with the card views. The loop creates a pair of `MatchCard` objects with the same `CardType` value. | ||
/// - parameter numberOfCards: The number of cards the game needs to create. Number of pairs is `numberOfCards / 2`. | ||
mutating func loadCards(numberOfCards: Int) { | ||
cards = [] | ||
|
||
for index in 0..<numberOfCards / 2 { | ||
let type = CardType.allCases[index % CardType.allCases.count] | ||
let card = MatchCard(type: type) | ||
cards.append(card) | ||
cards.append(card) | ||
} | ||
randomizeCards() | ||
|
||
delegate?.cardsDidLoad(cards) | ||
} | ||
|
||
// SOURCE: - https://www.dartmouth.edu/~chance/teaching_aids/Mann.pdf | ||
mutating func randomizeCards() { | ||
for _ in 0..<7 { | ||
cards.shuffle() | ||
} | ||
} | ||
|
||
mutating func checkForMatch(with flippedIndicies: [Int]) { | ||
attemptCounter.increment() | ||
|
||
if isMatched(flippedIndicies: flippedIndicies) { | ||
matchedCardsCount += 2 | ||
delegate?.cardsDidMatch(flippedIndicies, currentScore: attemptCounter.attemptCount) | ||
} else { | ||
delegate?.cardsDidNotMatch(flippedIndicies, currentScore: attemptCounter.attemptCount) | ||
} | ||
} | ||
|
||
/// The indicies are used to fetch the `MatchCard` from the `cards` array and adds the card to the `cardsToMatch` array. If all cards in the array have an equal `CardType`, the return value will be `true`. | ||
/// - parameter flippedIndicies: Rrepresent an array of each index flipped cards on the board. | ||
func isMatched(flippedIndicies: [Int]) -> Bool { | ||
var cardsToMatch = [MatchCard]() | ||
for index in flippedIndicies { | ||
cardsToMatch.append(cards[index]) | ||
} | ||
|
||
return cardsToMatch.allSatisfy { $0.type == cardsToMatch.first?.type} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
Braze-Demo-Match-Game-Content-Extension/View/MatchCardView.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import UIKit | ||
|
||
protocol MatchCardViewDelegate: class { | ||
func cardTapped(at index: Int) | ||
} | ||
|
||
class MatchCardView: UIView { | ||
|
||
// MARK: - Outlets | ||
@IBOutlet private weak var button: UIButton! | ||
|
||
// MARK: - Actions | ||
@IBAction func cardTapped(_ sender: UIButton) { | ||
delegate?.cardTapped(at: sender.tag) | ||
|
||
flipCard() | ||
} | ||
|
||
// MARK: - Variables | ||
private weak var delegate: MatchCardViewDelegate? | ||
|
||
override func awakeFromNib() { | ||
super.awakeFromNib() | ||
|
||
layer.cornerRadius = 5 | ||
layer.masksToBounds = true | ||
} | ||
|
||
func configureView(selectedImage: UIImage?, tag: Int, delegate: MatchCardViewDelegate? = nil) { | ||
self.delegate = delegate | ||
|
||
button.setImage(selectedImage, for: .selected) | ||
button.tag = tag | ||
} | ||
|
||
func flipCard() { | ||
button.isSelected.toggle() | ||
isUserInteractionEnabled = !button.isSelected | ||
|
||
UIView.transition(with: self, duration: 0.5, options: .transitionFlipFromRight, animations: nil, completion: nil) | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
Braze-Demo-Match-Game-Content-Extension/View/MatchCardView.xib
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> | ||
<device id="retina6_1" orientation="portrait" appearance="light"/> | ||
<dependencies> | ||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/> | ||
<capability name="Safe area layout guides" minToolsVersion="9.0"/> | ||
<capability name="System colors in document resources" minToolsVersion="11.0"/> | ||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> | ||
</dependencies> | ||
<objects> | ||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> | ||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> | ||
<view contentMode="scaleToFill" id="PLR-VX-SOo" customClass="MatchCardView" customModule="Braze_Demo_Match_Game_Content_Extension" customModuleProvider="target"> | ||
<rect key="frame" x="0.0" y="0.0" width="158" height="221"/> | ||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> | ||
<subviews> | ||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="NaN-Ty-6DP"> | ||
<rect key="frame" x="0.0" y="0.0" width="158" height="221"/> | ||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | ||
<state key="normal" image="card-background"/> | ||
<connections> | ||
<action selector="cardTapped:" destination="PLR-VX-SOo" eventType="touchUpInside" id="eCt-qw-jk2"/> | ||
</connections> | ||
</button> | ||
</subviews> | ||
<viewLayoutGuide key="safeArea" id="0hT-2F-PfG"/> | ||
<color key="backgroundColor" systemColor="systemBackgroundColor"/> | ||
<constraints> | ||
<constraint firstItem="NaN-Ty-6DP" firstAttribute="top" secondItem="PLR-VX-SOo" secondAttribute="top" id="4BD-of-5UY"/> | ||
<constraint firstItem="NaN-Ty-6DP" firstAttribute="leading" secondItem="PLR-VX-SOo" secondAttribute="leading" id="ZXR-BU-FVO"/> | ||
<constraint firstAttribute="trailing" secondItem="NaN-Ty-6DP" secondAttribute="trailing" id="hkL-Ep-rCY"/> | ||
<constraint firstAttribute="bottom" secondItem="NaN-Ty-6DP" secondAttribute="bottom" id="kgv-7X-7CM"/> | ||
</constraints> | ||
<nil key="simulatedTopBarMetrics"/> | ||
<nil key="simulatedBottomBarMetrics"/> | ||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> | ||
<connections> | ||
<outlet property="button" destination="NaN-Ty-6DP" id="fqN-OT-E7G"/> | ||
</connections> | ||
<point key="canvasLocation" x="139" y="50"/> | ||
</view> | ||
</objects> | ||
<resources> | ||
<image name="card-background" width="737" height="1041"/> | ||
<systemColor name="systemBackgroundColor"> | ||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> | ||
</systemColor> | ||
</resources> | ||
</document> |
Oops, something went wrong.