How to upload single and multiple images in iOS Swift, PHP and MySQL
In this article, we will discuss how you can upload single or multiple images from your iOS app in Swift, and save them in the MySQL database.
Solution for Swift UI
If you are developing your app in Swift UI, please follow this post.
When developing iOS applications, one of the most common features in almost all apps is to upload images. It can be in the form of a profile picture, or payment receipt or anything. Swift provides a built-in method to allow developers to add functionality. There users can select an image from their photo library. However, to upload multiple images, there is no built-in method in Swift at the time of writing this. But we have external libraries that allow users to pick multiple images.
Save image from iOS app
When it comes to saving the image in the database, there are again multiple choices. You can either convert the image in an encoded base64 string or you can send an image entirely as multipart. The second choice is not very often used because it has some drawbacks. The major one of them is that the image with very high quality and large size are unable to transfer. So we will choose the first method i.e. to convert an image into an encoded base64 string.
1. Single image upload
In this method, no external library has been used. All the functions required to select single image from gallery are already provided by Swift. We just need to implement 2 delegates UIImagePickerControllerDelegate and UINavigationControllerDelegate to our view controller that will be responsible to open the screen to gallery and also to return the selected image. We have created a simple button which when clicked will open the UIImagePickerController in current view controller. imagePickerController this is a delegate function present in UIImagePickerControllerDelegate that will return the selected image from photo library. We are displaying that image in our UIImageView for confirmation, and also to send to the server. Because we can convert an image to base64 string using its UIImageView object.
Then we are creating a button which when clicked will convert the image to base64 string and send the request to the server. For sending an HTTP request, you also have many choices, for example, you can use the Alamofire library. But to keep things simple, we are using Swift’s built-in NSURLConnection class. Before sending the request we are also displaying a loading dialog and when the response is received from the server, we are simply displaying an alert that the image has been uploaded.
Swift iOS
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
private var image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let btnSelectImage: UIButton = UIButton(frame: CGRect(x: 30, y: 100, width: view.frame.size.width - 60, height: 30))
btnSelectImage.setTitle("Select image", for: .normal)
btnSelectImage.setTitleColor(UIColor.systemBlue, for: .normal)
btnSelectImage.contentHorizontalAlignment = .center
btnSelectImage.addTarget(self, action: #selector(selectImage), for: .touchUpInside)
view.addSubview(btnSelectImage)
image = UIImageView(frame: CGRect(x: 30, y: btnSelectImage.frame.origin.y + btnSelectImage.frame.size.height + 30, width: 300, height: 300))
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
view.addSubview(image)
let btnUpload: UIButton = UIButton(frame: CGRect(x: 30, y: image.frame.origin.y + image.frame.size.height + 30, width: view.frame.size.width - 60, height: 40))
btnUpload.setTitle("Upload to server", for: .normal)
btnUpload.backgroundColor = UIColor.systemGreen
btnUpload.setTitleColor(UIColor.white, for: .normal)
btnUpload.addTarget(self, action: #selector(uploadToServer), for: .touchUpInside)
btnUpload.layer.cornerRadius = 5
view.addSubview(btnUpload)
}
@objc private func uploadToServer(sender: UITapGestureRecognizer) {
let imageData: Data = image.image!.pngData()!
let imageStr: String = imageData.base64EncodedString()
let alert = UIAlertController(title: "Loading", message: "Please wait...", preferredStyle: .alert)
present(alert, animated: true, completion: nil)
let urlString: String = "imageStr=" + imageStr
var request: URLRequest = URLRequest(url: URL(string: "http://172.20.10.2:8888/tutorials/single-multiple-image-upload/index.php")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = urlString.data(using: .utf8)
NSURLConnection.sendAsynchronousRequest(request, queue: .main, completionHandler: { (request, data, error) in
guard let data = data else {
return
}
let responseString: String = String(data: data, encoding: .utf8)!
print("my_log = " + responseString)
alert.dismiss(animated: true, completion: {
let messageAlert = UIAlertController(title: "Success", message: responseString, preferredStyle: .alert)
messageAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction!) in
//
}))
self.present(messageAlert, animated: true, completion: nil)
})
})
}
@objc private func selectImage(sender: UITapGestureRecognizer) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self as! UIImagePickerControllerDelegate & UINavigationControllerDelegate
imagePicker.sourceType = UIImagePickerController.SourceType.photoLibrary
imagePicker.allowsEditing = false
present(imagePicker, animated: true, completion: nil)
}
@objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let chosenImage = info[UIImagePickerController.InfoKey.originalImage.rawValue] as! UIImage
image.image = chosenImage
dismiss(animated: true, completion: nil)
}
}
PHP & MySQL
Make sure to change the server path with yours. If you are working on localhost, you can get your IP address by command prompt or terminal and running the command: ifconfig. In our PHP file, we are first receiving base64 string in a variable and then creating a path where the image will be stored. We need to create a folder named “images” in our server’s root folder.
Then we need to decode that base64 string into actual image file, so remove the base64 data part from a string, replace all empty spaces with + sign, and then decoding the base64 string, and finally saving the file in the images folder. We also need to save that file’s reference in our database, so we are simply making a connection with MySQL database and running an INSERT query to save the path of the image. Then we are sending the response as a string back to the iOS app. Make sure to change the database credentials as well as per your server.
<?php
$picture = $_POST["imageStr"];
$folder_path = "images/" . time() . ".png";
$data = str_replace('data:image/png;base64,', '', $picture);
$data = str_replace(' ', '+', $data);
$data = base64_decode($data);
file_put_contents($folder_path, $data);
$conn = mysqli_connect("localhost:8889", "root", "root", "tutorials");
$sql = "INSERT INTO images (path) VALUES ('$folder_path')";
mysqli_query($conn, $sql);
echo "Image has been uploaded";
?>
2. Multiple image upload
As discussed earlier, we need to use an external library to implement multiple image features in our iOS app. So we are going to use a library called BSImagePicker. It will be installed in our project using Cocoa pods, first, you need to initialize it. Open command prompt or terminal in your project’s root folder and run the following command:
pod init
This will create a Podfile (without any extension) in your project’s root folder. Open that file and include the library like the following, your project name could be different of course:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'MultipleImageUpload' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for MultipleImageUpload
pod "BSImagePicker", "~> 3.1"
end
Now run the command
pod update
to install the library. Once installed, close the project if already opened and now run the project using XCode but with the file ending with “.xcworkspace” NOT “.xcodeproj”. Now accessing the library requires user permission so open your “info.plist” file as source code and paste the following lines at the end of dict tag:
<key>NSPhotoLibraryUsageDescription</key>
<string>Why you want to access photo library</string>
Now open your view controller file and first import 2 files named BSImagePicker and Photos. When user select multiple images we can also show them in a collection view for a better user experience, for that purpose we need to implement 2 delegates, UICollectionViewDelegate and UICollectionViewDataSource. Creating a global variable for collection view so it can be accessible throughout the view controller class, colleection view has multiple cells and each cell has its own identifer and a class. For cell, we have created a class named ImageCell and it has only an image view because that is all we needed here.
Swift iOS
import UIKit
import BSImagePicker
import Photos
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
private var collectionView: UICollectionView!
private let identifier: String = "identifier"
private var selectedImages: [UIImage] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let btnSelectImage: UIButton = UIButton(frame: CGRect(x: 30, y: 100, width: view.frame.size.width - 60, height: 30))
btnSelectImage.setTitle("Select images", for: .normal)
btnSelectImage.setTitleColor(UIColor.systemBlue, for: .normal)
btnSelectImage.contentHorizontalAlignment = .center
btnSelectImage.addTarget(self, action: #selector(selectImages), for: .touchUpInside)
view.addSubview(btnSelectImage)
let flowLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
flowLayout.minimumLineSpacing = 10
flowLayout.scrollDirection = .horizontal
flowLayout.minimumInteritemSpacing = 10
flowLayout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
flowLayout.itemSize = CGSize(width: 300, height: 300)
collectionView = UICollectionView(frame: CGRect(x: 0, y: btnSelectImage.frame.origin.y + btnSelectImage.frame.size.height + 30, width: view.frame.size.width, height: 300), collectionViewLayout: flowLayout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.backgroundColor = UIColor.clear.withAlphaComponent(0)
collectionView.alwaysBounceHorizontal = true
collectionView.register(ImageCell.self, forCellWithReuseIdentifier: identifier)
view.addSubview(collectionView)
let btnUpload: UIButton = UIButton(frame: CGRect(x: 30, y: collectionView.frame.origin.y + collectionView.frame.size.height + 30, width: view.frame.size.width - 60, height: 40))
btnUpload.setTitle("Upload to server", for: .normal)
btnUpload.backgroundColor = UIColor.systemGreen
btnUpload.setTitleColor(UIColor.white, for: .normal)
btnUpload.addTarget(self, action: #selector(uploadToServer), for: .touchUpInside)
btnUpload.layer.cornerRadius = 5
view.addSubview(btnUpload)
}
@objc private func uploadToServer(sender: UITapGestureRecognizer) {
let alert = UIAlertController(title: "Loading", message: "Please wait...", preferredStyle: .alert)
present(alert, animated: true, completion: nil)
var imageStr: [String] = []
for a in 0..<self.selectedImages.count {
let imageData: Data = self.selectedImages[a].jpegData(compressionQuality: 0.1)!
imageStr.append(imageData.base64EncodedString())
}
guard let data = try? JSONSerialization.data(withJSONObject: imageStr, options: []) else {
return
}
let jsonImageString: String = String(data: data, encoding: String.Encoding.utf8) ?? ""
let urlString: String = "imageStr=" + jsonImageString
var request: URLRequest = URLRequest(url: URL(string: "http://172.20.10.2:8888/tutorials/single-multiple-image-upload/multiple.php")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = urlString.data(using: .utf8)
NSURLConnection.sendAsynchronousRequest(request, queue: .main, completionHandler: { (request, data, error) in
guard let data = data else {
return
}
let responseString: String = String(data: data, encoding: .utf8)!
print("my_log = " + responseString)
alert.dismiss(animated: true, completion: {
let messageAlert = UIAlertController(title: "Success", message: responseString, preferredStyle: .alert)
messageAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction!) in
//
}))
self.present(messageAlert, animated: true, completion: nil)
})
})
}
@objc private func selectImages(sender: UITapGestureRecognizer) {
let imagePicker = ImagePickerController()
presentImagePicker(imagePicker, select: { (asset) in
}, deselect: { (asset) in
}, cancel: { (assets) in
}, finish: { (assets) in
self.selectedImages = []
let options: PHImageRequestOptions = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
for asset in assets {
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: options) { (image, info) in
self.selectedImages.append(image!)
self.collectionView.reloadData()
}
}
})
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return selectedImages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let data: UIImage = selectedImages[indexPath.item]
let cell: ImageCell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! ImageCell
cell.image.image = data
return cell
}
}
class ImageCell: UICollectionViewCell {
var image: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupViews()
}
private func setupViews() {
image = UIImageView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
image.clipsToBounds = true
image.contentMode = .scaleAspectFill
addSubview(image)
}
}
PHP & MySQL
As we are sending multiple images in base64 string. So, we have to loop through all and save them one-by-one in the server as well as in MySQL database. The code for this is pretty much same as the previous file.
<?php
$conn = mysqli_connect("localhost:8889", "root", "root", "tutorials");
$imageStr = json_decode($_POST["imageStr"]);
for ($a = 0; $a < count($imageStr); $a++)
{
$folder_path = "images/" . $a . "-" . time() . ".png";
$picture = $imageStr[$a];
$data = str_replace('data:image/png;base64,', '', $picture);
$data = str_replace(' ', '+', $data);
$data = base64_decode($data);
file_put_contents($folder_path, $data);
$sql = "INSERT INTO images (path) VALUES ('$folder_path')";
mysqli_query($conn, $sql);
}
echo "Image has been uploaded";
?>
So that’s how you can upload single or multiple images from your iOS app in Swift, and save them in the MySQL database.
[wpdm_package id=’62’]