Morgan's Reviews - iOS Entertainment Review App
Quick Summary
A native iOS review application built in Swift that allows a reviewer to create, edit, and publish reviews of movies, TV shows, games, and other entertainment. Users can browse reviews with ratings, images, and detailed commentary, while the reviewer maintains editorial control through a secure sign-in system.
Tech Stack: Swift, UIKit, Xcode, iOS SDK
Status: ✅ Complete and Deployed (2019)
Platform: iOS (iPhone/iPad)
GitHub: View Source Code
Video Demo
The Challenge
Design and implement a dual-interface iOS application that serves two distinct user groups:
- Reviewers (authenticated): Create, edit, and delete reviews with full editorial control
- General Users: Browse and read published reviews in a clean, intuitive interface
The app needed to provide:
- Secure authentication preventing unauthorized edits
- Rich media support (images from photo library)
- Custom star rating system with accessibility features
- Persistent storage of review content
- Seamless navigation between viewing and editing modes
System Architecture
Application Structure
The app follows the Model-View-Controller (MVC) pattern with seven main components:
Models:
Review.swift- Core data model with validation logic
Views:
RatingControl.swift- Custom UIStackView-based star rating componentReviewTableViewCell.swift- Custom table cell for review list
Controllers:
ReviewTableViewController.swift- Main list view (7 pre-loaded reviews)ReviewUserrViewController.swift- Read-only review detail viewReviewViewController.swift- Full edit/create review interfaceSigninViewController.swift- Reviewer authentication for new reviewsSigninUserViewController.swift- Reviewer authentication for editing existing reviews
Navigation Flow:
ReviewTableViewController (Main List)
├── [Tap Review] → ReviewUserrViewController (Read-Only)
│ └── [Edit Button] → SigninUserViewController
│ └── [Auth Success] → ReviewViewController
└── [+ Button] → SigninViewController
└── [Auth Success] → ReviewViewController
Key Features
1. Custom Star Rating System
Built a reusable RatingControl component using UIStackView:
Features:
- 5-star rating scale (0-5)
- Three button states: empty, filled, highlighted
- Toggle functionality (tap same rating to reset to 0)
- VoiceOver accessibility support
- Configurable star count and size via
@IBInspectable - User interaction enable/disable for read-only mode
Implementation Highlights:
- Dynamic button array management
- Custom image asset loading from bundle
- Programmatic constraint setup
- State synchronization with
didSetproperty observer
2. Dual Authentication System
Implemented two separate sign-in flows to handle different use cases:
SigninViewController (New Reviews):
- Simple credential validation (username: “good”, password: “app”)
- Enables “Done” button only after successful authentication
- Direct segue to
ReviewViewControllerfor creation
SigninUserViewController (Edit Existing):
- Receives review object from
ReviewUserrViewController - Passes review data to
ReviewViewControllerupon authentication - Maintains review context through navigation chain
3. Review Management System
Create Operation:
- Text field validation (title and paragraph required)
- Image picker integration for photo library access
- Real-time save button state management
- Navigation title updates as title is typed
Update Operation:
- Pre-populates all fields with existing review data
- Enables delete button (disabled during creation)
- Preserves rating and image during edits
Delete Operation:
- Sets rating to 31 (sentinel value) to signal deletion
ReviewTableViewControllerdetects sentinel and removes row- Smooth fade animation on deletion
4. Image Handling
Integrated UIImagePickerController for media management:
@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
nameTextField.resignFirstResponder()
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .savedPhotosAlbum
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
Features:
- Tap gesture recognizer on UIImageView
- Dismisses keyboard before showing picker
- Extracts original unedited image
- Persistent storage in Review model
Technical Implementation
Review Data Model
class Review {
var title: String
var photo: UIImage?
var rating: Int
var reviewwords: String
init?(title: String, photo: UIImage?, rating: Int, reviewwords: String) {
guard !title.isEmpty && !reviewwords.isEmpty else {
return nil
}
guard (((rating >= 0) && (rating <= 5)) || rating == 31) else {
return nil
}
// Initialization...
}
}
Validation Rules:
- Title and review text cannot be empty
- Rating must be 0-5 (or 31 for deletion sentinel)
- Failable initializer returns
nilfor invalid data
Table View Data Source
The main table view controller manages a dynamic array of reviews:
Cell Configuration:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? ReviewTableViewCell else {
fatalError("The dequeued cell is not an instance of ReviewTableViewCell")
}
let review = reviews[indexPath.row]
cell.nameLabel.text = review.title
cell.photoImageView.image = review.photo
cell.ratingControl.rating = review.rating
cell.backgroundColor = UIColor(red: 0/255.0, green: 255/255.0, blue: 127/255.0, alpha: 1.0)
return cell
}
Unwind Segue Logic
Implemented sophisticated unwind logic to handle all navigation scenarios:
@IBAction func unwindToReviewList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? ReviewViewController, let review = sourceViewController.review {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
if review.rating == 31 {
// Delete operation
reviews.remove(at: selectedIndexPath.row)
tableView.deleteRows(at: [selectedIndexPath], with: .fade)
} else {
// Update operation
reviews[selectedIndexPath.row] = review
tableView.reloadRows(at: [selectedIndexPath], with: .none)
}
} else {
// Create operation
let newIndexPath = IndexPath(row: reviews.count, section: 0)
reviews.append(review)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
}
Smart Detection:
- Checks if
selectedIndexPathexists to determine create vs. edit - Uses rating sentinel (31) to detect delete operations
- Applies appropriate animation for each operation type
User Interface Design
Visual Identity
Color Scheme:
- Primary: Spring Green (RGB: 0, 255, 127)
- Applied consistently across all view controllers
- High contrast for readability
Typography & Layout:
- Standard iOS fonts for familiarity
- Clear visual hierarchy (title → image → rating → body text)
- Ample spacing for comfortable reading
Accessibility Features
VoiceOver Support in RatingControl:
button.accessibilityLabel = "Set \(index + 1) star rating"
button.accessibilityValue = "\(rating) stars set"
button.accessibilityHint = rating == index + 1 ? "Tap to reset the rating to zero" : nil
Features:
- Descriptive labels for each star button
- Current rating announcement
- Reset hint when tapping active rating
- Screen reader friendly navigation
Pre-Loaded Content
The app ships with 7 sample reviews covering various entertainment:
| Title | Rating | Category |
|---|---|---|
| DC’s best movie yet: Shazam! | ⭐⭐⭐⭐⭐ | Movie |
| The Biggest Error in Avengers Endgame! | ⭐⭐⭐⭐ | Movie |
| John Wick 3 and the Endless Action! | ⭐⭐⭐⭐ | Movie |
| Black Ops 4 Where are the Changes? | ⭐⭐ | Game |
| Morgan’s Reviews the Best App Ever | ⭐⭐⭐⭐⭐ | App |
| Computer Science the best subject | ⭐⭐⭐⭐⭐ | Education |
| SuperStore the soon to be superstar show | ⭐⭐⭐⭐ | TV Show |
Challenges & Solutions
Challenge: Dual User Interface Paradigms
Problem: The app needed to serve both authenticated reviewers with full CRUD capabilities and general users with read-only access, without confusing the two experiences.
Solution:
- Created separate view controllers for read-only (
ReviewUserrViewController) and editable (ReviewViewController) interfaces - Disabled star rating interaction in user view:
userrating.isUserInteractionEnabled = false - Implemented two authentication flows to handle creating new reviews vs. editing existing ones
- Used segue chains to maintain context through authentication
Result: Clean separation of concerns with intuitive navigation for both user types
Challenge: Review State Persistence Through Navigation
Problem: Reviews needed to maintain their state when passed through multiple view controllers (TableView → UserView → SignIn → EditView)
Solution:
// In ReviewUserrViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let reviewDetailViewController = segue.destination as? SigninUserViewController else {
fatalError("Unexpected Destination: \(segue.destination)")
}
let selectedReview = self.review
reviewDetailViewController.review = selectedReview
}
Pattern Used:
- Pass review object through each segue’s
prepare(for:sender:) - Maintain optional
var review: Review?in each controller - Type-safe unwrapping with guard statements
Result: Seamless data flow through complex navigation chains
Challenge: Delete Operation Without Explicit Delete Button in Table
Problem: Standard iOS pattern uses swipe-to-delete, but app needed delete functionality only for authenticated reviewers within the edit interface.
Solution:
- Added delete button in
ReviewViewController(only visible when editing) - Used sentinel value (rating = 31) to signal deletion intent
unwindToReviewListchecks rating and removes row if sentinel detected- Preserved table view’s clean interface without exposed delete actions
Code Pattern:
// In ReviewViewController
if let button = sender as? UIButton, button === delete {
review = Review(title: title, photo: photo, rating: 31, reviewwords: reviewwords)
}
// In ReviewTableViewController
if review.rating == 31 {
reviews.remove(at: selectedIndexPath.row)
tableView.deleteRows(at: [selectedIndexPath], with: .fade)
}
Result: Secure delete functionality without compromising user interface simplicity
Challenge: Save Button State Management
Problem: Users could potentially save incomplete reviews without both title and review text.
Solution:
- Implemented
updateSaveButtonState()method - Text field delegate monitors editing state
- Disables save button during active editing
- Re-enables only when both fields contain text
private func updateSaveButtonState() {
let text = nameTextField.text ?? ""
let text2 = paragraph.text ?? ""
saveButton.isEnabled = !text.isEmpty && !text2.isEmpty
}
func textFieldDidBeginEditing(_ textField: UITextField) {
saveButton.isEnabled = false
}
func textFieldDidEndEditing(_ textField: UITextField) {
updateSaveButtonState()
}
Result: Prevents invalid review submissions while providing clear visual feedback
Code Architecture
File Structure (600+ lines total)
morgansreview/
├── Models/
│ └── Review.swift (30 lines)
├── Views/
│ ├── RatingControl.swift (130 lines)
│ └── ReviewTableViewCell.swift (25 lines)
├── Controllers/
│ ├── ReviewTableViewController.swift (140 lines)
│ ├── ReviewUserrViewController.swift (50 lines)
│ ├── ReviewViewController.swift (140 lines)
│ ├── SigninViewController.swift (45 lines)
│ └── SigninUserViewController.swift (50 lines)
└── AppDelegate.swift (40 lines)
Design Patterns Used
Model-View-Controller (MVC):
- Clean separation between data (Review), presentation (Custom views), and logic (Controllers)
Delegation:
- UITextFieldDelegate for keyboard management
- UIImagePickerControllerDelegate for photo selection
- UITableViewDelegate/DataSource for list management
Target-Action:
- Button taps in RatingControl
- Bar button items in navigation
Optional Chaining:
- Safe unwrapping throughout:
let text = nameTextField.text ?? "" - Guard statements for type-safe downcasting
Failable Initializers:
- Review model validates data at creation time
- Returns nil for invalid input
What I Learned
This project taught me:
iOS Development Fundamentals
- UIKit framework and view hierarchy
- Storyboard-based UI design with programmatic constraints
- Navigation controller stack management
- Delegate pattern and protocol conformance
Swift Language Features
- Optional handling and guard statements
- Property observers (didSet, willSet)
- Failable initializers for validation
- Type safety and casting
User Experience Design
- Authentication flow design
- State management across view controllers
- Accessibility considerations (VoiceOver)
- Visual feedback for user actions
Table View Mastery
- Custom cell creation and registration
- Dynamic row insertion/deletion with animation
- Cell reuse and performance optimization
- Section and row management
Image Handling
- UIImagePickerController integration
- Photo library permissions
- Image asset management in bundle
- Button state images
Original Project Goals vs. Reality
From the Proposal (April 2019):
Original Vision:
- Review app for movies, TV shows, games, products, books
- Alternative to trailers that give away plots
- Help people find suitable entertainment
- Build reviews based on personality matching
What Was Achieved:
- ✅ Multi-category review system (movies, shows, games)
- ✅ Honest, spoiler-free review format
- ✅ Clean, article-style presentation
- ✅ Star rating system for quick assessment
- ✅ Rich media support with images
- ✅ Editorial control through authentication
What Was Simplified:
- ❌ Public user-submitted reviews (remained single-reviewer focused)
- ❌ Personality-based recommendation algorithm
- ❌ Cross-platform deployment (iOS only)
Reflection: The simplification from the original proposal was intentional and wise for a school project timeline. The focus shifted from a social review platform to a personal review blog in app format—a more achievable scope that still demonstrated all core iOS development skills.
Future Improvements
If I were to extend this project today, I would:
- Firebase Backend Integration - Replace in-memory storage with cloud database for true persistence
- SwiftUI Rewrite - Modernize using declarative UI framework
- User Accounts - Multiple reviewers with profiles
- Search & Filtering - Find reviews by category, rating, or keyword
- Rich Text Editor - Markdown support for formatted reviews
- Share Functionality - Export reviews to social media
- Dark Mode Support - Respect system appearance preferences
- iPad Optimization - Split-view layout for larger screens
- Localization - Multi-language support
- Analytics - Track most-viewed reviews and user engagement
Technologies Used
Languages & Frameworks:
- Swift 5.0
- UIKit
- Foundation
Development Tools:
- Xcode 10.2
- Interface Builder (Storyboards)
- iOS Simulator
Key APIs:
- UITableView & UITableViewController
- UIImagePickerController
- UINavigationController
- UITextField & UITextFieldDelegate
- Auto Layout (NSLayoutConstraint)
- Bundle & Asset Catalog
Design Patterns:
- Model-View-Controller (MVC)
- Delegation
- Target-Action
- Observer (Property Observers)
Project Timeline
April 2019:
- Week 1-2: Proposal, design, and Swift learning
- Week 3: Core Review model and RatingControl implementation
- Week 4: Table view and navigation setup
May 2019:
- Week 1: Authentication system and edit/create flows
- Week 2-3: Polish, testing, and pre-loaded content
- Week 4: App Fair presentation and final submission
Total Development Time: ~6 weeks (part-time, during school)
Takeaway
This project demonstrates end-to-end iOS application development: from initial proposal and UX planning, through implementation of custom UI components and complex navigation flows, to deployment of a polished, functional app. It showcases my ability to work with iOS frameworks, implement secure authentication patterns, manage complex state across multiple view controllers, and deliver a user-friendly solution within a school project timeline.
The app successfully achieved its core goal: providing a platform for honest, spoiler-free entertainment reviews that help users make informed viewing decisions—all wrapped in a clean, accessible iOS interface.