New to iOS development so I'm a little confused on this part of implement the point of sale sdk
Usage
Swift
Import Declaration: import SquarePointOfSaleSDK
// Replace with your app's URL scheme.
let yourCallbackURL = URL(string: "your-url-scheme://")!
// Your client ID is the same as your Square Application ID.
// Note: You only need to set your client ID once, before creating your first request.
SCCAPIRequest.setClientID("YOUR_CLIENT_ID")
do {
// Specify the amount of money to charge.
let money = try SCCMoney(amountCents: 100, currencyCode: "USD")
// Create the request.
let apiRequest =
try SCCAPIRequest(
callbackURL: yourCallbackURL,
amount: money,
userInfoString: nil,
merchantID: nil,
notes: "Coffee",
customerID: nil,
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnAutomaticallyAfterPayment: false
)
// Open Point of Sale to complete the payment.
try SCCAPIConnection.perform(apiRequest)
} catch let error as NSError {
print(error.localizedDescription)
}
Finally, implement the UIApplication delegate method as follows:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
guard let sourceApplication = options[.sourceApplication] as? String,
sourceApplication.hasPrefix("com.squareup.square") else {
return false
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}
I'm a little confused where I put these respective pieces of code. My best guess is that the UIApplication delegate goes into AppDelegate.swift?
You can set the client ID in the appDidFinishLaunching method in AppDelegate since it only needs to be set once.
The code that creates and executes the API request can go in whatever view controller you'd like to use to initiate the payment.
You are correct, the last method should be added to your AppDelegate.
Related
This app is intentionally tiny - the UI is just a button to initiate the api call. The appDelegate simply shows a console log proving the callback was executed. I did this because I can not get the Square payment system to work even thought the code and url setup etc looks right to me. I am wondering if I misunderstand something in the documentation.
My app ID is: sq0idp-TbgQGqSrC84qkfcXSTntNg
bundleID is: com.craig.POSSDKTest2
My info.plist setup is a screenshot:info.plist and URLScheme setup
My code in the api calling VC:
// ViewController.swift
// POSSDKTest2
//
// Created by Dr Craig E Curphey on 2022-06-14.
//
import UIKit
import SquarePointOfSaleSDK
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}//------------------------------------
#IBAction func doPaymentTapped(_ sender: Any) {
//------ API to Square right here ******-----------------
let callbackURL = URL(string: "POSSDKTest2Scheme://")!
SCCAPIRequest.setApplicationID("sq0idp-TbgQGqSrC84qkfcXSTntNg")
var money: SCCMoney?
do {
// Specify the amount of money to charge - $1.00
let todaysFeePennies = 100
money = try SCCMoney(amountCents: todaysFeePennies, currencyCode: "CAD")
} catch {
print("money conversion failed.")
}
let LoginID = "TestPatient"
//log values used to console:
print("Initiating payment api request")
print("callback: \(callbackURL)")
print("money: \(money!)")
print("LoginID: TestPatient")
print("Creating payment api request")
do
{
let apiRequest =
try SCCAPIRequest(
callbackURL: callbackURL,
amount: money!,
userInfoString: LoginID,
locationID: "",
notes: "TestingSDK2",
customerID: "tester2",
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnsAutomaticallyAfterPayment: false,
disablesKeyedInCardEntry: false,
skipsReceipt: false
)
print("api initiating!")
try SCCAPIConnection.perform(apiRequest)
} catch let error as NSError {
print("Error detected: \(error.localizedDescription)")
}//end API
print("End of payment api function.")
}//------------------------------------
}
Here is my appDelegate code - I just want to see if I get a callback - I'll add the code to process the response later ...
// AppDelegate.swift
// POSSDKTest2
//
// Created by Dr Craig E Curphey on 2022-06-14.
//
var squareTokenID = "not specified - let it fail - I just want a callback!"
import UIKit
import SquarePointOfSaleSDK
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
//-------------------------------------------
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("Callback was executed!")
guard SCCAPIResponse.isSquareResponse(url) else {
print("\(SCCAPIResponse.isSquareResponse(url))")
return true
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}//-------------------------------------------------------
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}//-------------------------------------------------------
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}//--------------------------------------------------------
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}//---------------------------------------------------------
}
Screenshot of console log from trying to do the api:
Console log
The work environment is Xcode 13.3, Swift, macOSMonterey 12.1 beta, iPad 10.2" (I think) mounted in a Square iPad kiosk with attached card reader, running iOS 15.5.
I would be very happy to send the whole project in a zip file to anyone who would be willing to take a look into why it is failing.
I am attempting to implement Square Connect iOS SDK, and after implementing and clicking the pay button it opens up the Square Payment app and redirects to a blank page .. have you guys had the same issues ?
My App delegate has the proper section:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
guard let sourceApplication = options[.sourceApplication] as? String,
sourceApplication.hasPrefix("com.squareup.square") else {
return false
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}
I have created a proper URL scheme inside the Square portal. Also my view controller has the correct code:
func charge()
{
// connect v1
if let callbackURL = URL(string: "myscheme://")
{
do
{
SCCAPIRequest.setClientID("xxxxxxxxxxx")
let amount = try SCCMoney(amountCents: 100, currencyCode: "USD")
let request = try SCCAPIRequest(
callbackURL: callbackURL,
amount: amount,
userInfoString: nil,
locationID: nil,
notes: "Purchase for cleaning",
customerID: nil, supportedTenderTypes: .all,
clearsDefaultFees: true,
returnAutomaticallyAfterPayment: true)
try SCCAPIConnection.perform(request)
}
catch let error as NSError {
print(error.localizedDescription)
}
}
}
https://snag.gy/R4A30L.jpg
Andrei
You need to make sure you delete all apps from the phone with having duplicate bundle ids. This way you will make sure you go back to the original app which triggered the payment.
So i have a FirstViewController where i download a video with the progress view and progress is working fine using this code
func startDownloading() {
let download = Downloads(url: videoUrl!.absoluteString!)
download.downloadTask = self.downloadsSession.downloadTaskWithURL(videoUrl!)
download.downloadTask!.resume()
download.isDownloading = true
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
// 1
print("URLSession Completed for url \(downloadTask.originalRequest?.URL?.absoluteString)")
if let originalURL = downloadTask.originalRequest?.URL?.absoluteString,
destinationURL = localFilePathForUrl(originalURL) {
let fileManager = NSFileManager.defaultManager()
do {
try fileManager.removeItemAtURL(destinationURL)
} catch {
// Non-fatal: file probably doesn't exist
}
do {
try! fileManager.copyItemAtURL(location, toURL: destinationURL)
} catch let error as NSError {
print("Could not copy file to disk: \(error.localizedDescription)")
}
}
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print("URLSession inProgress \(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite))")
if let downloadUrl = downloadTask.originalRequest?.URL?.absoluteString,
let download = activeDownloads[downloadUrl] {
//THIS SETS THE PROGRESS
download.progress = Float(totalBytesWritten)/Float(totalBytesExpectedToWrite)
self.downloadView.state = .Downloading
self.downloadView.setProgress(Double(totalSize)!, animated: true)
}
}
now this code updates FirstViewControllers downloadView.progress correctly but what i want is when i go to SecondViewController i should get the progress of this ongoing download in SecondVC too without starting the download progress again (i know downloading again would be very dumb).
The best way is to separate your network request manager code from the view controller:
Create a separate class to manage the requests, and move your delegate code there.
In the didWriteData method, use NSNotificationCenter to broadcast the notification to any interested view class or make your first view controller notify the second one if it exists.
In each of your view controller classes, register for the notification and when you receive it, update the status accordingly.
I have a terms and conditions view controller that is the entry point to my app. It checks:
if( FBSDKAccessToken.currentAccessToken() != nil){
print("here")
dispatch_async(dispatch_get_main_queue()){
[unowned self] in
self.performSegueWithIdentifier("jump", sender: self)
}
}
else{
print("user not logged in")
}
}
Here is my AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
Parse.initializeWithConfiguration(parseConfiguration)
PFFacebookUtils.initializeFacebookWithApplicationLaunchOptions(launchOptions)
if( FBSDKAccessToken.currentAccessToken() == nil){
print("fuck")
}
return true
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return FBSDKApplicationDelegate.sharedInstance().application(
application,
openURL: url,
sourceApplication: sourceApplication,
annotation: annotation)
}
func applicationWillResignActive(application: UIApplication) {
FBSDKAppEvents.activateApp()
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
FBSDKAppEvents.activateApp()
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
let loginManager: FBSDKLoginManager = FBSDKLoginManager()
loginManager.logOut()
}
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "mm.MunchMatch" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("MunchMatch", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
}
Does anyone have any idea why my current access token is evaluating to nil even though the user has already logged in?
Why are you loging out the user in applicationWillTerminate ?
calling loginManager.logOut() will set the currentAccesToken to nil.
I have created a custom DataManager class. Inside it I want to fetch data in a method and return an NSData object to convert to JSON afterwards.
I have tried to get the data using the completionHandler but no luck:
class func fetchData() -> NSData? {
var session = NSURLSession.sharedSession(),
result = NSData?()
let DataURL : NSURL = NSURL(string: "http://...file.json")!
let sessionTask = session.dataTaskWithURL(DataURL, completionHandler: { (data: NSData!, response: NSURLResponse!, error: NSError!) -> Void in
result = data
})
sessionTask.resume()
return result
}
The dataTask runs asynchronously. That means that the completion handler closure will not be called by the time you return from fetchData. Thus, result will not have been set yet.
Because of this, you should not try to retrieve data synchronously from an asynchronous method. Instead, you should employ an asynchronous completion handler pattern yourself:
class func fetchData(completion: #escaping (Data?, Error?) -> Void) {
let session = URLSession.shared
let url = URL(string: "http://...file.json")!
let task = session.dataTask(with: url) { data, response, error in
completion(data, error)
}
task.resume()
}
And you'd call it like so:
MyClass.fetchData { data, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}
// use `data` here; remember to dispatch UI and model updates to the main queue
}
// but do not try to use `data` here
...
FYI, for the original pre-Swift 3 syntax, see previous revision of this answer.