0

I am an intermediate student in iOS development, I am trying to make a method that uploads an image to a server. I understand the server side scripting in PHP.

But when I am following a tutorial to upload an image in Xcode, I don't really grasp about NSData, NSObject, NSMutableData, NSString, it seems the tutorial doesn't really the fundamental aspect of NSData, NSMutableData, NSString...

If want to take beautiful display, I should learn about auto layout, collection view etc.

So, what kind of topic in iOS development that I should learn to really understand about these things step by step? It seems that I never learn specifically about these things. I don't know where to start.

The code to upload an image is like this:

func createBodyWithParameters(parameters: [String: String]?, filePathKey: String?, imageDataKey: NSData, boundary: String) -> NSData {
        let body = NSMutableData();

        if parameters != nil {
            for (key, value) in parameters! {
                body.appendString("--\(boundary)\r\n")
                body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
                body.appendString("\(value)\r\n")
            }
        }

                let filename = "user-profile.jpg"
                let mimetype = "image/jpg"

                body.appendString("--\(boundary)\r\n")
                body.appendString("Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n")
                body.appendString("Content-Type: \(mimetype)\r\n\r\n")
                body.appendData(imageDataKey)
                body.appendString("\r\n")



        body.appendString("--\(boundary)--\r\n")

        return body
    }



    func generateBoundaryString() -> String {
        return "Boundary-\(NSUUID().UUIDString)"
    }


}


extension NSMutableData {

    func appendString(string: String) {
        let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
        appendData(data!)
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Alexa289
  • 8,089
  • 10
  • 74
  • 178
  • 2
    Don't use any of those `NS*` classes in Swift. Use native Swift types. – rmaddy Dec 15 '17 at 04:35
  • 1
    You should start dropping the NS prefix and use `Data` instead of `NSData`. If you need it to be mutable just declare it as `var`. The same applies for `NSString`, you should use `String` instead. – Leo Dabus Dec 15 '17 at 04:35
  • If you are comfortable with Objective-C or Swift, you should read Apple's documentation. There is a ton of stuff available online as well regarding these core foundation classes, you should try multiple samples codes. – Vakas Dec 15 '17 at 05:36
  • You should find a more contemporary tutorial, because if you have one that is suggesting `NSData`, etc., it's pretty outdated in a world where Swift is evolving quickly. For example, [this shows you](https://stackoverflow.com/a/26163136/1271826) what a Swift 3/4 rendition might look like (as well as showing you the Swift 2 rendition, like what you've got here, so you get a sense of how it's changed). Unfortunately, inquiries about off site resources like tutorials is off-topic for Stack Overflow. – Rob Dec 15 '17 at 06:04

1 Answers1

5

Learning about objects you have described may mean many things. If you are after their capabilities then the documentation should be enough. But if you are more after what is under the hood and why we need these objects then I could only suggest you to look into some older language like C.

These objects NSData, NSMutableData, NSString are all data containers, buffers. But NSObject is just a base class from which all other objects inherit.

So a bit about NSData and NSMutableData: In C when creating a raw buffer you use malloc which reserves a chunk in memory which you may use as you please. Once done you need to call free to release that memory or you will have a memory leak.

void *voidPointer = malloc(100); // Reserved 100 bytes of whatever data
int *intPointer = (int *)malloc(sizeof(int)*100); // Reserved enough bytes to fit 100 integers whatever their size may be
intPointer[13] = 1; // You may use these as normal array
free(voidPointer); // Free this memory
free(intPointer); // Free this memory

So NSData is basically a wrapper for that and does all of it for you. You may even access the raw pointer by calling bytes on NSData object. Then the mutable version NSMutableData is just a subclass which has some additional functionality. You may actually append data. From what is under the hood appending data is not so simple. You need to allocate a new memory chunk, copy old data to it, copy new data and release the previous memory chunk.

void *currentData = malloc(100); // Assume we have some data
void *dataToAppend = malloc(100); // Another chunk of data we want to append
void *combinedData = malloc(200); // We need a larger buffer now
memcpy(combinedData, currentData, 100); // Copy first 100 bytes to new data
memcpy(combinedData+100, dataToAppend, 100); // Copy next 100 bytes to new data
free(currentData); // Free old data
free(dataToAppend); // Free old data
... use combinedData here ...
free(combinedData); // Remember to free combined data once done

These are all really simple methods but they may already be pain to write and it is easy to produce bugs doing so. So NSData or NSMutableData and even Data in Swift are all just data containers that make your developer life easier. And in Objective-C conversion from data to C buffers is as easy as it gets:

NSData *myData = [NSData dataWithBytes:myDataPointer length:myDataLength];
void *myRawPointer = [myData bytes];

The NSString is not really that different. In C we again have character pointer which is used as string so we write something like:

char *myText = "Some text";

These are a bit special, a convenience really. We could as well do:

char *myText = (char *)malloc(sizeof(char)*100);

And then fill the data character by character:

myText[0] = 'S';
myText[1] = 'o';
myText[2] = 'm';
...
myText[9] = 't';
myText[10] = '\0'; // We even need to set a null terminator at the end

and then we needed to free the memory again... But never mind the C strings, NSString is again a wrapper that is responsible to allocate the memory, assign data and do whatever you want with it. It has may methods you can use simply to make your life easier.

As to the code you posted it is a combination of the two. In your case your API accepts images as multipart form data requests which you may understand as a raw image file with a few texts added around it just to explain what the data contains. It is one of a generally used way but not the only one. You might as well just post the raw image data or you might even post a JSON containing a base64 string encoded data. Also as usually these texts are represented as an utf8 encoded data.

In the end it is a set of standards that are generally used so our computers may communicate between each other. Your image is most likely defined by a standard from png or jpg on how to present it with a string of bytes, your strings are defined by utf8 standard and your whole request body is defined by some HTTP standards (not even sure what part of it is that). And the objects you use and want to learn about are just some helpers for achieving your result. Understanding them in most cases is like understanding a screwdriver; you won't need to in most cases, but you do need to know they exist and you need to know when to use them.

The code itself you posted is relatively bad but should do its job. For a beginner it might be a bit confusing even. Probably a more logical pseudocode for this solution would be something like:

let imageData: Data // My image data
let headerString: String // Text I need to put before the image data
let footerString: String // Text I need to put after the image data

var dataToSend: Data = Data() // Generate data object
dataToSend.append(headerString.utf8Data) // Append header
dataToSend.append(imageData) // Append raw data
dataToSend.append(footerString.utf8Data) // Append footer

I hope this clears up a few things.

Matic Oblak
  • 16,318
  • 3
  • 24
  • 43
  • Thank you very much Matic, it is clear now about the object. i feel bad that i can only copy and paste that code especially that HTTP part, I have tried to read about HTTP protocol, and multipart/form-data. but i still don't get it. I usually just used enctype= multipart/form-data as the atribute in the 'form' of HTML to upload an image. I have tried to read about HTTP protocol, and multipart/form-data. but i still don't get it. like I said before,I usually just used enctype= multipart/form-data as the atribute in the HTML form. what topic should I learn to understand this? Thank you :) – Alexa289 Dec 18 '17 at 03:41
  • @Alexa289 I am not a backend developer but have connected to many remote APIs by now. I doubt there is a good "topic" you could read to find all these situations, there are standards but there are way too many of them. It is all about the tools you use to create your API and those are which you need to understand. You need to get used to creating your documentation which can in most cases be auto-generated and should have all the necessary data to create a correct request. – Matic Oblak Dec 18 '17 at 08:43