Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The first draft of ImageCompressionStrategy #1

Open
wants to merge 4 commits into
base: Load_UIKit_View_Controller_on_SwiftUI-Based_Project
Choose a base branch
from
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
10 changes: 9 additions & 1 deletion MyApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
9A135C032CBE774500808EB3 /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A135C022CBE774300808EB3 /* ImageProcessor.swift */; };
9AAC417C2C74668B00AC0A60 /* MyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAC417B2C74668B00AC0A60 /* MyApp.swift */; };
9AAC417E2C74668B00AC0A60 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AAC417D2C74668B00AC0A60 /* ContentView.swift */; };
9AAC41802C74668C00AC0A60 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9AAC417F2C74668C00AC0A60 /* Assets.xcassets */; };
Expand All @@ -15,6 +16,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
9A135C022CBE774300808EB3 /* ImageProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProcessor.swift; sourceTree = "<group>"; };
9AAC41782C74668B00AC0A60 /* MyApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
9AAC417B2C74668B00AC0A60 /* MyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyApp.swift; sourceTree = "<group>"; };
9AAC417D2C74668B00AC0A60 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -57,6 +59,7 @@
9AAC417B2C74668B00AC0A60 /* MyApp.swift */,
9AAC417D2C74668B00AC0A60 /* ContentView.swift */,
9AAC418A2C7466F000AC0A60 /* UIKitViewControllerWrapper.swift */,
9A135C022CBE774300808EB3 /* ImageProcessor.swift */,
9AAC417F2C74668C00AC0A60 /* Assets.xcassets */,
9AAC41812C74668C00AC0A60 /* MyApp.entitlements */,
9AAC41822C74668C00AC0A60 /* Preview Content */,
Expand Down Expand Up @@ -100,7 +103,7 @@
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1540;
LastUpgradeCheck = 1600;
TargetAttributes = {
9AAC41772C74668B00AC0A60 = {
CreatedOnToolsVersion = 15.4;
Expand Down Expand Up @@ -144,6 +147,7 @@
files = (
9AAC418B2C7466F000AC0A60 /* UIKitViewControllerWrapper.swift in Sources */,
9AAC417E2C74668B00AC0A60 /* ContentView.swift in Sources */,
9A135C032CBE774500808EB3 /* ImageProcessor.swift in Sources */,
9AAC417C2C74668B00AC0A60 /* MyApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -185,6 +189,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
Expand Down Expand Up @@ -246,6 +251,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
Expand Down Expand Up @@ -273,6 +279,7 @@
CODE_SIGN_ENTITLEMENTS = MyApp/MyApp.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"MyApp/Preview Content\"";
DEVELOPMENT_TEAM = 99WD9D6Y46;
ENABLE_HARDENED_RUNTIME = YES;
Expand Down Expand Up @@ -311,6 +318,7 @@
CODE_SIGN_ENTITLEMENTS = MyApp/MyApp.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "\"MyApp/Preview Content\"";
DEVELOPMENT_TEAM = 99WD9D6Y46;
ENABLE_HARDENED_RUNTIME = YES;
Expand Down
Binary file not shown.
21 changes: 21 additions & 0 deletions MyApp/Assets.xcassets/Round_logo.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "Round_logo.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
136 changes: 136 additions & 0 deletions MyApp/ImageProcessor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//
// ImageProcessor.swift
// MyApp
//
// Created by Cong Le on 10/15/24.
//

import UIKit

/**
Defines the interface for image compression strategies. This protocol ensures that all concrete strategies adhere to a consistent method signature.
*/
protocol ImageCompressionStrategy {
/// Compresses the given image and returns the compressed data or an error.
/// - Parameter image: The `UIImage` to compress.
/// - Returns: A `Result<Data, Error>` containing the compressed image data on success, or an `Error` on failure.
func compress(image: UIImage) -> Result<Data, Error>
}

/**
Implements JPEG compression strategy. Conforms to the `ImageCompressionStrategy` protocol.
*/
class JPEGCompression: ImageCompressionStrategy {

/// Compresses the input image using JPEG compression with a quality of 0.8.
/// - Parameter image: The `UIImage` to be compressed.
/// - Returns: A `Result<Data, Error>` containing the compressed JPEG data or an error if compression fails.
func compress(image: UIImage) -> Result<Data, Error> {
guard let data = image.jpegData(compressionQuality: 0.8) else {
return .failure(NSError(domain: "JPEGCompression", code: 0, userInfo: [NSLocalizedDescriptionKey: "JPEG compression failed"]))
}
return .success(data)
}
}

/**
Implements PNG compression strategy. Conforms to the `ImageCompressionStrategy` protocol.
*/
class PNGCompression: ImageCompressionStrategy {

/// Compresses the input image using PNG compression.
/// - Parameter image: The `UIImage` to be compressed.
/// - Returns: A `Result<Data, Error>` containing the compressed PNG data or an error if compression fails.
func compress(image: UIImage) -> Result<Data, Error> {
guard let data = image.pngData() else {
return .failure(NSError(domain: "PNGCompression", code: 0, userInfo: [NSLocalizedDescriptionKey: "PNG compression failed"]))
}
return .success(data)
}
}



// MARK: - Context Class
/**
The context class that uses a concrete strategy to compress images. Manages the currently selected compression strategy.
*/
class ImageProcessor {
/// The currently selected image compression strategy.
var compressionStrategy: ImageCompressionStrategy

/**
Initializes the `ImageProcessor` with a specific compression strategy.
- Parameter compressionStrategy: The initial `ImageCompressionStrategy` to use.
*/
init(compressionStrategy: ImageCompressionStrategy) {
self.compressionStrategy = compressionStrategy
}

/**
Processes the given image using the currently assigned compression strategy.
- Parameter image: The `UIImage` to process.
- Returns: A `Result<Data, Error>` containing the compressed image data or an error if processing fails.
*/
func processImage(image: UIImage) -> Result<Data, Error> {
return compressionStrategy.compress(image: image)
}

/**
Sets a new compression strategy for the image processor.
- Parameter strategy: The new `ImageCompressionStrategy` to use.
*/
func setCompressionStrategy(strategy: ImageCompressionStrategy) {
self.compressionStrategy = strategy
}
}

/**
Represents the available image compression types. Used for selecting the appropriate strategy.
Conforms to `CaseIterable` and `RawRepresentable` for easy iteration and string conversion.
*/
enum CompressionType: CaseIterable, RawRepresentable {
case jpeg, png

typealias RawValue = String

/// The string representation of the compression type.
var rawValue: String {
switch self {
case .jpeg: return "jpeg"
case .png: return "png"
}
}

/**
Initializes a `CompressionType` from a raw string value.
- Parameter rawValue: The string representation of the compression type.
*/
init?(rawValue: String) {
switch rawValue {
case "jpeg": self = .jpeg
case "png": self = .png
default: return nil
}
}
}

// MARK: - Strategy Factory (Advanced Usage)
/**
A factory struct responsible for creating concrete `ImageCompressionStrategy` instances based on a given `CompressionType`.
*/
struct CompressionStrategyFactory {

/**
Creates and returns an `ImageCompressionStrategy` based on the specified compression type.
- Parameter type: The `CompressionType` indicating the desired strategy.
- Returns: An optional `ImageCompressionStrategy` instance. Returns `nil` if the compression type is not supported.
*/
static func createStrategy(for type: CompressionType) -> ImageCompressionStrategy? {
switch type {
case .jpeg: return JPEGCompression()
case .png: return PNGCompression()
}
}
}

52 changes: 51 additions & 1 deletion MyApp/UIKitViewControllerWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,59 @@ struct UIKitViewControllerWrapper: UIViewControllerRepresentable {

// Example UIKit view controller
class MyUIKitViewController: UIViewController {


override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
// Additional setup

// Simulate dynamic selection by iterating through all compression types
for compressionType in CompressionType.allCases {
performImageCompression(using: compressionType)
}
}

// Function to simulate usage of Image Compression Strategy
func performImageCompression(using compressionType: CompressionType) {
// Using the factory to create the selected compression strategy:
guard let strategy = CompressionStrategyFactory.createStrategy(for: compressionType) else {
print("Unsupported compression type selected.")
return
}
let imageProcessor = ImageProcessor(compressionStrategy: strategy)

// Replace "Round_logo" with the actual image name in your asset catalog
// or use another method to load the UIImage
guard let image = UIImage(named: "Round_logo") else {
print("Failed to load the image.")
return
}

// Retrieve original image data based on the selected compression type
let originalImageData: Data?
switch compressionType {
case .jpeg:
// Assuming the original image is best represented as JPEG for comparison
originalImageData = image.jpegData(compressionQuality: 1.0)
case .png:
// Assuming the original image is best represented as PNG for comparison
originalImageData = image.pngData()
}

if let originalData = originalImageData {
print("Compression Type: \(compressionType.rawValue.uppercased())")
print("Original image data size: \(originalData.count) bytes")
} else {
print("Failed to retrieve original image data.")
}

// Perform compression using the selected strategy
let result = imageProcessor.processImage(image: image)
switch result {
case .success(let compressedData):
print("Compressed image data size: \(compressedData.count) bytes\n")
case .failure(let error):
print("Image compression failed: \(error.localizedDescription)\n")
}
}
}