From 1916a8f7e055628fd6af5bf0353510d3b597445d Mon Sep 17 00:00:00 2001 From: Franck Letellier Date: Wed, 5 May 2021 14:57:52 +0200 Subject: [PATCH 1/4] Updated segmentation service to public --- Example/PhotoRoomKitDemo/Podfile.lock | 4 +-- PhotoRoomKit.xcodeproj/project.pbxproj | 8 ++--- ...er.swift => PhotoRoomViewController.swift} | 9 +++-- Sources/iOS/SegmentationService.swift | 35 +++++++++++-------- 4 files changed, 30 insertions(+), 26 deletions(-) rename Sources/iOS/{EditingViewController.swift => PhotoRoomViewController.swift} (97%) diff --git a/Example/PhotoRoomKitDemo/Podfile.lock b/Example/PhotoRoomKitDemo/Podfile.lock index 7ba4e79..199df56 100644 --- a/Example/PhotoRoomKitDemo/Podfile.lock +++ b/Example/PhotoRoomKitDemo/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - PhotoRoomKit (0.0.1) + - PhotoRoomKit (0.0.2) DEPENDENCIES: - PhotoRoomKit (from `../../`) @@ -9,7 +9,7 @@ EXTERNAL SOURCES: :path: "../../" SPEC CHECKSUMS: - PhotoRoomKit: fe0eb2b1760e36a57685d8fda81456edb7d83b63 + PhotoRoomKit: 92fce543303acefcdc6ec194108de2f1c9402cc1 PODFILE CHECKSUM: 75a88f0e658c982c9ca2e49331e2562d62c6ab27 diff --git a/PhotoRoomKit.xcodeproj/project.pbxproj b/PhotoRoomKit.xcodeproj/project.pbxproj index 96c27a6..2642708 100644 --- a/PhotoRoomKit.xcodeproj/project.pbxproj +++ b/PhotoRoomKit.xcodeproj/project.pbxproj @@ -8,9 +8,9 @@ /* Begin PBXBuildFile section */ 95097DE52641494A00854E2B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 95097DE42641494A00854E2B /* Assets.xcassets */; }; + 95097E2A2642C83900854E2B /* PhotoRoomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95097E292642C83900854E2B /* PhotoRoomViewController.swift */; }; 9571030B26412ACF00319C32 /* SegmentationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9571030826412ACF00319C32 /* SegmentationService.swift */; }; 9571030C26412ACF00319C32 /* ImageHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9571030926412ACF00319C32 /* ImageHelpers.swift */; }; - 9571030D26412ACF00319C32 /* EditingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9571030A26412ACF00319C32 /* EditingViewController.swift */; }; D284B1461F7908B300D94AF3 /* SharedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D284B1321F7906DA00D94AF3 /* SharedTests.swift */; }; D284B1471F7908B800D94AF3 /* iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D284B1361F7906DA00D94AF3 /* iOSTests.swift */; }; D5B2E8AA1C3A780C00C0327D /* PhotoRoomKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5B2E89F1C3A780C00C0327D /* PhotoRoomKit.framework */; }; @@ -28,9 +28,9 @@ /* Begin PBXFileReference section */ 95097DE42641494A00854E2B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 95097E292642C83900854E2B /* PhotoRoomViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoRoomViewController.swift; sourceTree = ""; }; 9571030826412ACF00319C32 /* SegmentationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentationService.swift; sourceTree = ""; }; 9571030926412ACF00319C32 /* ImageHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageHelpers.swift; sourceTree = ""; }; - 9571030A26412ACF00319C32 /* EditingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditingViewController.swift; sourceTree = ""; }; D284B12F1F7906DA00D94AF3 /* Info-iOS-Tests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS-Tests.plist"; sourceTree = ""; }; D284B1321F7906DA00D94AF3 /* SharedTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedTests.swift; sourceTree = ""; }; D284B1361F7906DA00D94AF3 /* iOSTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iOSTests.swift; sourceTree = ""; }; @@ -113,7 +113,7 @@ D5C6296A1C3A809D007F7B7C /* iOS */ = { isa = PBXGroup; children = ( - 9571030A26412ACF00319C32 /* EditingViewController.swift */, + 95097E292642C83900854E2B /* PhotoRoomViewController.swift */, 9571030926412ACF00319C32 /* ImageHelpers.swift */, 9571030826412ACF00319C32 /* SegmentationService.swift */, ); @@ -249,8 +249,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 95097E2A2642C83900854E2B /* PhotoRoomViewController.swift in Sources */, 9571030B26412ACF00319C32 /* SegmentationService.swift in Sources */, - 9571030D26412ACF00319C32 /* EditingViewController.swift in Sources */, 9571030C26412ACF00319C32 /* ImageHelpers.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/iOS/EditingViewController.swift b/Sources/iOS/PhotoRoomViewController.swift similarity index 97% rename from Sources/iOS/EditingViewController.swift rename to Sources/iOS/PhotoRoomViewController.swift index f1e3477..dc259da 100644 --- a/Sources/iOS/EditingViewController.swift +++ b/Sources/iOS/PhotoRoomViewController.swift @@ -7,7 +7,7 @@ import UIKit -final public class EditingViewController: UIViewController { +final public class PhotoRoomViewController: UIViewController { enum ViewState { case success case loading @@ -140,7 +140,8 @@ final public class EditingViewController: UIViewController { viewState = .loading //remove background - SegmentationService.segment(image: originalImage, apiKey: apiKey, onCompletion: { (image, error) in + let segmentationService = SegmentationService(apiKey: apiKey) + segmentationService.segment(image: originalImage) { (image, error) in DispatchQueue.main.async { if let error = error { self.viewState = .error(error: error) @@ -154,9 +155,7 @@ final public class EditingViewController: UIViewController { self.viewState = .success }) } - }) - - // Do any additional setup after loading the view. + } } // MARK: - Constraints diff --git a/Sources/iOS/SegmentationService.swift b/Sources/iOS/SegmentationService.swift index 05ff11c..41ecb3a 100644 --- a/Sources/iOS/SegmentationService.swift +++ b/Sources/iOS/SegmentationService.swift @@ -8,22 +8,27 @@ import Foundation import UIKit -struct SegmentationService { - typealias SegmentationCallback = (UIImage?, Error?) -> Void +public final class SegmentationService { + public typealias SegmentationCallback = (UIImage?, Error?) -> Void - static func segment(image: UIImage, - apiKey: String, + private let apiKey: String + private enum K { + static let hostURL = URL(string: "https://sdk.photoroom.com/v1/segment")! + } + + public init(apiKey: String) { + self.apiKey = apiKey + } + + public func segment(image: UIImage, onCompletion: @escaping SegmentationCallback) { - let host = "https://sdk.photoroom.com/v1/segment" - if apiKey == "" { + + guard apiKey.isEmpty == false else { onCompletion(nil, SegmentationError.noAPIKey) return } - guard let url = URL(string: host) else { - onCompletion(nil, SegmentationError.invalidData) - return - } - var request = URLRequest(url: url) + + var request = URLRequest(url: K.hostURL) request.httpMethod = "POST" request.timeoutInterval = 30.0 @@ -36,8 +41,8 @@ struct SegmentationService { return } - let boundary = generateBoundary() - let body = createDataBody(with: media, boundary: boundary) + let boundary = Self.generateBoundary() + let body = Self.createDataBody(with: media, boundary: boundary) request.httpBody = body @@ -71,11 +76,11 @@ struct SegmentationService { } - static func generateBoundary() -> String { + private static func generateBoundary() -> String { return "Boundary-\(NSUUID().uuidString)" } - static func createDataBody(with media: Media, boundary: String) -> Data { + private static func createDataBody(with media: Media, boundary: String) -> Data { let lineBreak = "\r\n" var body = Data() From c0615b1c27d4c3a21338dc47d768b5ae2863c155 Mon Sep 17 00:00:00 2001 From: Franck Letellier Date: Wed, 5 May 2021 15:13:01 +0200 Subject: [PATCH 2/4] Added some code documentation --- Sources/iOS/PhotoRoomViewController.swift | 7 +++++-- Sources/iOS/SegmentationService.swift | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Sources/iOS/PhotoRoomViewController.swift b/Sources/iOS/PhotoRoomViewController.swift index dc259da..96009d3 100644 --- a/Sources/iOS/PhotoRoomViewController.swift +++ b/Sources/iOS/PhotoRoomViewController.swift @@ -6,7 +6,7 @@ // import UIKit - +/// Ready to use ViewController to remove the background final public class PhotoRoomViewController: UIViewController { enum ViewState { case success @@ -113,7 +113,10 @@ final public class PhotoRoomViewController: UIViewController { }() // MARK: - Life Cycle - + /// - Parameters: + /// - image: The image you want to remove background + /// - apiKey: PhotoRoom API key + /// - completionHandler: Called once the background removal has been completed. Will not return if errored public init(image: UIImage, apiKey: String, completionHandler: ((UIImage) -> Void)? = nil) { self.originalImage = image self.apiKey = apiKey diff --git a/Sources/iOS/SegmentationService.swift b/Sources/iOS/SegmentationService.swift index 41ecb3a..5a42d35 100644 --- a/Sources/iOS/SegmentationService.swift +++ b/Sources/iOS/SegmentationService.swift @@ -8,7 +8,10 @@ import Foundation import UIKit +/// Service to remove background from any image public final class SegmentationService { + + /// Handler containing the possible segmented image public typealias SegmentationCallback = (UIImage?, Error?) -> Void private let apiKey: String @@ -16,10 +19,16 @@ public final class SegmentationService { static let hostURL = URL(string: "https://sdk.photoroom.com/v1/segment")! } + /// - Parameters: + /// - apiKey: PhotoRoom API key public init(apiKey: String) { self.apiKey = apiKey } + /// Segment the image to a white background + /// - Parameters: + /// - image: The image you want to remove background + /// - onCompletion: Called once the segmentation is over. See `SegmentationCallback` for more detail public func segment(image: UIImage, onCompletion: @escaping SegmentationCallback) { From e400ae5c09b70e5aeb193661d1202d0a6ce385f3 Mon Sep 17 00:00:00 2001 From: Franck Letellier Date: Wed, 5 May 2021 15:13:33 +0200 Subject: [PATCH 3/4] Fixed Demo project --- .../PhotoRoomKitDemo/Sources/ViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Example/PhotoRoomKitDemo/PhotoRoomKitDemo/Sources/ViewController.swift b/Example/PhotoRoomKitDemo/PhotoRoomKitDemo/Sources/ViewController.swift index 7cb2abb..1b02697 100644 --- a/Example/PhotoRoomKitDemo/PhotoRoomKitDemo/Sources/ViewController.swift +++ b/Example/PhotoRoomKitDemo/PhotoRoomKitDemo/Sources/ViewController.swift @@ -32,9 +32,11 @@ class ViewController: UIViewController, UINavigationControllerDelegate { } func removeBackground(_ originalImage: UIImage) { - let resultViewController = EditingViewController(image: originalImage, - apiKey: K.photoRoomAPIKey, - completionHandler: onImageEdited) + let resultViewController = PhotoRoomViewController(image: originalImage, + apiKey: K.photoRoomAPIKey) { [weak self] image in + self?.onImageEdited(image) + + } present(resultViewController, animated: true) } From 45c5bdf6d5ab547bb67ef0153f33cbf4b3963b6c Mon Sep 17 00:00:00 2001 From: Franck Letellier Date: Wed, 5 May 2021 15:32:20 +0200 Subject: [PATCH 4/4] Updating readme + podspec --- PhotoRoomKit.podspec | 2 +- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/PhotoRoomKit.podspec b/PhotoRoomKit.podspec index 85c6d57..41f03b6 100644 --- a/PhotoRoomKit.podspec +++ b/PhotoRoomKit.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "PhotoRoomKit" s.summary = "Fast background removal API." - s.version = "0.0.2" + s.version = "0.0.3" s.homepage = "https://github.com/PhotoRoom/PhotoRoomKit-Swift" s.license = { :type => 'MIT' } s.author = { "PhotoRoom" => "ios@photoroom.com" } diff --git a/README.md b/README.md index 0c8f4a3..eb99880 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,52 @@ github "PhotoRoom/PhotoRoomKit" **PhotoRoomKit** can also be installed manually. Just download and drop `Sources` folders in your project. +## Usage + +First you will need an API Key, go to [our API website](https://photoroom.com/api) to get your own PhotoRoom key. + +Then, you can either use the provided `PhotoRoomViewController`, or use the API wrapper + +#### PhotoRoomViewController + +Just present `PhotoRoomViewController` and handle the callback +```swift +func removeBackground(_ originalImage: UIImage) { + let controller = PhotoRoomViewController(image: originalImage, + apiKey: 'YOUR_API_KEY') { [weak self] image in + self?.onImageEdited(image) + + } + present(controller, animated: true) +} + +func onImageEdited(_ editedImage: UIImage) { + // Handle your segmented image +} +``` +When using the built-in view controller, Photoroom attribution is done for you, no need for extra work. + +#### API wrapper + +You can also use the API wrapper directly. +```swift +let segmentationService = SegmentationService(apiKey: apiKey) +segmentationService.segment(image: originalImage) { (image, error) in + DispatchQueue.main.async { + if let error = error { + // An error occured + } + guard let image = image else { + // No image returned + return + } + // All good + } +} +``` + +⚠️ If you use the API wrapper, you'll need to provide correct attribution according to [our API guideline](https://www.notion.so/photoroom/API-Documentation-public-4eb3e45d9c814f92b6392b7fd0f1d51f#7ac1c3bd30fd426ea092e126f4b59c77). + ## Author PhotoRoom, hello@photoroom.com