How to convert svg+xml base64 to UIImage? - ios

I simply have string like this:
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxwYXRoIGlkPSJzdmdfMSIgc3Ryb2tlPSIjMDAwIiBmaWxsPSIjZmZmIi8+CiAgPHJlY3Qgcng9IjYyIiBzdHJva2Utd2lkdGg9IjEwIiBpZD0ic3ZnXzIiIGhlaWdodD0iMzAzIiB3aWR0aD0iNjI5IiB5PSIxMTIiIHg9IjkxIiBzdHJva2U9IiMwMDAiIGZpbGw9IiNmZmYiLz4KICA8ZGVmcz4KICA8cmVjdCByeT0iNjMiIHJ4PSI2MyIgc3Ryb2tlLXdpZHRoPSIxMCIgaWQ9InJlY3QiIGhlaWdodD0iMTI2IiB3aWR0aD0iMTI2IiB5PSIxNTAiIHg9IjE0MCIgc3Ryb2tlPSIjMDAwIiBmaWxsPSIjZmZmIi8+CiAgPGNsaXBQYXRoIGlkPSJjbGlwIj4KICAgICAgPHVzZSB4bGluazpocmVmPSIjcmVjdCIvPgogICAgPC9jbGlwUGF0aD4KPC9kZWZzPgoKICA8dXNlIHhsaW5rOmhyZWY9IiNyZWN0IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iYmxhY2siLz4KICA8aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFHUUFBQUJrQ0FJQUFBRC9nQUlEQUFBRENrbEVRVlI0bk95ZFhVaFRjUnlHZC9SUUpFSE1WU3BwdFJiWWwwWWZHcWxZTVNnS2pNZ1V2SWt5R3RLY2RKZkVvckpGUVlyV3hReGNRUVcxQzhHaE80WE1ncUNJRWxtME1ZTkZ3VENFR3BSRk9jaWllOEVYM3Z2M3VYM1lPRHo4THM2Qi8va2QwNUdldFMxTUpERUI3RlQ0TTdDMWxoMVlZNkFOMkZ1aExMQ1B5bnFBUFhiMU1MQ2w2OUJ2dTE5ZkF6WVBPREVQeFNKUUxBTEZJbEFzQXNVaVVDd0N4U0pRTEFKejZWY1Q2TWtYZm1BYk8xY0JHNjFZQVd5aXJSN1l2dXFMd0s3SlRRRTduZDRQN0pleE9MQ0ZSY2VCMVdRUktCYUJZaEVvRm9GaUVTZ1dnV0lSS0JhQlloR1lXNWNiUUR2dHU0RDFqZXdEZHMvWlA4Q1d1SXVBdlRsVEJ1eUE3eVN3VnFRZjJPR0NsY0F1c3lXQTFXUVJLQmFCWWhFb0ZvRmlFU2dXZ1dJUktCYUJZaEdZUXpjK0FqMmJPd2RzUjBzcHNET2JEZ0o3enhVR2RuaXpHOWdsK2FlQWRlU2paNUx5WURHd3llYTd3R3F5Q0JTTFFMRUlGSXRBc1FnVWkwQ3hDQlNMUUxFSXpPTFlKNkNUbFU1Z2ExNjlBL2IzNmc1Z2I5dlBBMXUrRzkyRkY0NC9BN1lsTUFoc3dJbm1JNXhCWjJ3MFdRU0tSYUJZQklwRm9GZ0Vpa1dnV0FTS1JhQllCRVpYNmkzUU93TG9qdlpJNndmMDMyUG90UHJjNDFGZzMzaEhnTTAyUGdIMlVud2pzTDJIMERWWDJaSFZaQkVvRm9GaUVTZ1dnV0lSS0JhQlloRW9Gb0ZpRVJqcEp2VGVaMmNFdlcvNnZtRVJzTk1KZEtiOHg0TmVZUE1HMFlhY254UGJnYTNMZEFQYjAzNEMyQzAxZTlGVkFTZm1vVmdFaWtXZ1dBU0tSYUJZQklwRm9GZ0Vpa1ZndEhxalFLZlFvaHJiL2ZwSllFYzlCNEIxUjBQQTlnODFBN3Y0NzNWZy9hZVBBcnZUampaZ1B1eERwNFkwV1FTS1JhQllCSXBGb0ZnRWlrV2dXQVNLUmFCWUJFWW02UVg2elBNVXNITU5QbURqbmlwZ3ZlMXJnYlVLU29EMVAwVjdLaS9IcW9FTlhrRDdNYk5KQzFoTkZvRmlFU2dXZ1dJUktCYUJZaEVvRm9GaUVTZ1dnZWtJTlFFZGZPa0M5b3BaQit6M0N2UjFwRzIxNkp0TnY2dzd3RlorV3cvc1A5ODRzQnM4WGNER1hEbGdOVmtFaWtXZ1dBU0tSYUJZQklwRm9GZ0Vpa1dnV0FUL0F3QUEvLy9yWFpLVzhmMG5SQUFBQUFCSlJVNUVya0pnZ2c9PSIgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIHk9IjE2MCIgeD0iMTYwIiBjbGlwLXBhdGg9InVybCgjY2xpcCkiLz4KCiAgPHRleHQgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgdGV4dC1hbmNob3I9InN0YXJ0IiBmb250LWZhbWlseT0iTm90byBTYW5zIEpQIiBmb250LXNpemU9IjI0IiBpZD0ic3ZnXzQiIHk9IjM0MiIgeD0iMTc5IiBzdHJva2Utd2lkdGg9IjAiIHN0cm9rZT0iIzAwMCIgZmlsbD0iIzAwMDAwMCI+YXNnZGhqYWdhc2Rqa2FzZGg8L3RleHQ+CiAgPHRleHQgdHJhbnNmb3JtPSJtYXRyaXgoMTguMDU3LCAwLCAwLCA3LjM3MzIsIC04NDI5LjI0LCAtMTkxNS40NSkiIHhtbDpzcGFjZT0icHJlc2VydmUiIHRleHQtYW5jaG9yPSJzdGFydCIgZm9udC1mYW1pbHk9IidEYW5jaW5nIFNjcmlwdCciIGZvbnQtc2l6ZT0iMSIgaWQ9InN2Z183IiB5PSIyOTAuNTEwNjEiIHg9IjQ5Ny4wODEyNyIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2U9IiMwMDAiIGZpbGw9IiNmZmYiPmU8L3RleHQ+CiA8L2c+Cjwvc3ZnPg=="
How do I convert it into UIImage using SVGKit cocoapod library?
For now I simply use:
var image: UIImage? {
guard let imageEncodedString = imageEncodedString else {
return nil
}
return SVGKImage(data: Data(base64Encoded: imageEncodedString))?.uiImage //crash here inside init
}

The beginning of the string is not base64, that's why it's failing. You need to remove first data:image/svg+xml;base64,.
You have a Data URI.
It should be like that:
data:[<media type>][;base64],<data>
You currently have:
data:image/svg+xml;base64,theBase64Image
Now, you can either remove always data:image/svg+xml;base64, from your string:
let dataURI = "data:image/svg+xml;base64,theBase64Image"
let base64String = dataURI.replacingOccurrences(of: "data:image/svg+xml;base64,", with: "")
You can also check before hand if hasPrefix() to ensure that it has the expected dataURI. You could also use a Regex, NSScanner, etc. to do so.
But since you expect SVG in base64, hasPrefix() and replacingOccurrences(of:with:) should be enough.

Related

Converting Base64 to data returning nil

I am trying to convert base64 string to image, I checked the json response, base64 string is not empty, but Data returns nil in between.
let base64String = image.Image_bytes_1
var data = Data(base64Encoded: base64String!, options: .ignoreUnknownCharacters)
let image = UIImage(data: data! as Data)
imageView.image = image
at line#4 getting this below error
Thread 3: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
The Data initializer you are using, init(base64Encoded:options:) is declared as init?(base64Encoded base64Data: Data, options: Data.Base64DecodingOptions = [])
That's known as a "failable initializer", which means it can return nil. (note the question mark after init in the declaration.)
If the base64 data is not well-formed, it may fail and return nil.
Stop using force-unwrapping. Rewrite using optional binding:
if let base64String = image.Image_bytes_1,
let data = Data(base64Encoded: base64String, options: .ignoreUnknownCharacters),
let image = UIImage(data: data) {
imageView.image = image
} else {
print("Unable to load base64 data and convert to image")
}
Edit:
As workingdog pointed out in their comment, your code does not make sense:
let base64String = image.Image_bytes_1 // line 1
var data = Data(base64Encoded: base64String!, options: .ignoreUnknownCharacters) // line 2
let image = UIImage(data: data! as Data) // line 3
imageView.image = image // line 4
Line 1 references something called image. You don't show the code that declares it, so we don't know what it is.
Then in line 3, you declare a new local constant also named image. If the original image is an instance variable of your class, and the code you posted is inside a function, you are creating a new local variable that overrides the other variable called image. I strongly advise against using the same variable name in different levels of scope. That is confusing at best, and causes errors at worst.

Converting string (Contains json string) to NSURL in swift

I have PAI its Implemented in .NET.
one of the web service url is like this
http://123.321.33/UploadCitizenImage?jsonString={\"Mobile\":\"12345678\", \"fileName\":\"7661832460_05072018.png\"}
while converting above string to URL in swift, app going crash.
for more info check this
The URL(string:) initializer returns an optional since the parsing of the string may fail. In that case, nil is returned. That's exactly what's happening here since the string you are providing is not a valid URL: there are several characters in the query that are not allowed there and need to be replaced: { as %7B, " as %22, space as %20 and } as %7D.
So the initializer returns nil. Next thing you do is force unwrap via the ! operator. But force-unwrapping a nil is illegal and is why you get the crash.
If you want to create an URL, please look into the URLComponents class which does all the necessary escaping for you so you don't need to care about it. The queryItems property is of particular interest for you, it's an array of URLQueryItem.
Please do something like that,
let jsonString = "jsonString={\"Mobile\":\"12345678\", \"fileName\":\"7661832460_05072018.png\"}" as String
let urlEncoadedJson = jsonString.addingPercentEncoding(withAllowedCharacters:.urlHostAllowed)
let urls = NSURL(string:"http://123.321.33/UploadCitizenImage?\(urlEncoadedJson ?? "")")
First convert your json into encodedJson then add into your url.
Do let me know if there is some issue.
You can try this,
let string = "http://123.321.33/UploadCitizenImage?jsonString={\"Mobile\":\"12345678\", \"fileName\":\"7661832460_05072018.png\"}"
let escapedString = string.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let url = URL(string: escapedString!)!
print(url)
Output will be like this,
http://123.321.33/UploadCitizenImage?jsonString=%7B%22Mobile%22:%2212345678%22,%20%22fileName%22:%227661832460_05072018.png%22%7D

Converting a multi dimensional string array into doubles

I have been trying to parse a CSV file forever and I am almost there. I have gotten it to a multi-dimensional array of strings using this code:
let path = Bundle.main.url(forResource: "BaseballSimStats", withExtension: "csv")
var file = String()
do {
file = try String(contentsOf: path!)
print(file)
} catch {
print(error)
}
let stringarray = file.components(separatedBy: "\n").map{ $0.components(separatedBy: ",") }
Now the last step is to turn it into a Double. I am using this code:
probs = Double[[stringarray]]
I get an error saying that the type has no subscript errors. I get rid of the subscript references and the error goes away. Why is this error here and how can I get rid of it? Thanks!
I used .map() to map the String into a Double, this should work for nested array
var strArray = [["1.00000","1.10000"],["2.00000","2.10000"]]
var doubleArray = strArray.map { (arr: Array) -> Array<Any> in
return arr.map({ (value: String) -> Double in
return Double(value)!
})
}
print(strArray)
print(doubleArray)
I am not sure if the double map was needed.
I am not a swift guru but this code should help you achieve what you want..
I'm not familiar with Double[[stringarray]] syntax so I don't know how that's supposed to work. I do know you can't just cast between array types.
The simplest way is probably to wrap the innermost call with Double.init():
file.components(separatedBy: "\n").map{ $0.components(separatedBy: ",").map { Double($0)! }}
Of course, there's a bit more to CSV than just splitting on commas and assuming everything is a valid number, so I'd highly recommend using an existing CSV parsing library for any real data.

What is causing items from UIPasteboard to be converted to NSConcreteMutableData?

I'm running the following in a playground (I've tested in Xcode 7.3.1 as well as Xcode 8.1 and see the same behavior):
import UIKit
let key: String = "some_key"
let value: String = "some_value"
UIPasteboard.general.items = [[key: value]]
let item = UIPasteboard.general.items.first
if let item = UIPasteboard.general.items.first {
switch item[key] {
case let x as String:
print("This is expected")
case let x as Any:
type(of: x)
print("This is unexpected")
default:
print("This is unexpected")
}
} else {
print("This is unexpected")
}
And I notice that the String that I put into the pasteboard actually gets bridged back out as NSConcreteMutableData.
My questions are the following:
Is this caused by an internal UIPasteboard implementation (i.e. explicitly converting from NSString to NSConcreteMutableData), or is this standard ObjC-Swift bridging behavior?
How can I work around this to store custom key/value pairs in UIPasteboard?
Here's what this looks like in a playground, for easy reference:
The Data you get for item[key] is simply the UTF-8 encoded value for the string.
If you add the following case, you will see that:
case let x as Data:
let str = String(data: x, encoding: .utf8)
print("str = \(str)")
The cause of the confusion is that the keys used by the items property are UTIs, not random keys. If you change your key to public.text then your code will work as expected.
Normally, you would not put a string on the pasteboard using the items property. You would use the string property to read and write the value.
UIPasteboard.general.string = "Hello"
let aStr = UIPasteboard.general.string
print("aStr = \(aStr)")
Doing this avoids the need to specify a UTI and it avoids replacing all existing items on the pasteboard.

insert a String inside another String

How can I make this:
var originalString = "http://name.domain.com/image.jpg"
becomes this:
originalString = "http://name.domain.com/image_new.jpg"
I could not find any document about the new Range<String.Index> in Swift.
This is not a problem in Obj-C, but without any reference about Range, it suddenly becomes so confusing.
Thanks.
Edit:
Well, thanks for these solutions. However, let me give you more details about this question.
After uploading an image to server, it responds back with a String link, like above, and the image name is a random string.
The server also generates different versions of uploaded image (like Flickr). In order to get these images, I have to append a suffix into image name, it looks like this:
originalString = "http://image.domain.com/randomName_large.jpg" or "http://image.domain.com/randomName_medium.jpg"
So that's why I need to insert a String into another String. My solution is find the first . by scan the link backwardly and append a suffix before it, but the new Range<String.Index> makes it confusing.
There are some nice and useful methods on NSString that you should be able to use:
let originalString: NSString = "http://name.domain.com/image.jpg"
let extension = originalString.pathExtension // "jpg"
let withoutExt = originalString.stringByDeletingPathExtension() // "http://name.domain.com/image"
let imageName = withoutExt.lastPathComponent // "image"
let withoutFilename = withoutExt.stringByDeletingLastPathComponent() // "http://name.domain.com/"
let newString = withoutFilename
.stringByAppendingPathComponent("\(imageName)_new")
.stringByAppendingPathExtension(extension)
I only typed this into the browser (it's untested) but it should give you an idea...
This can be done with String manipulation functions. But what if the string
is
var originalString = "http://images.domain.com/image.jpg"
? You probably do not want to replace the first or all occurrences of the string
"image" here.
A better tool for this purpose might be NSURLComponents, which lets you
modify all components of a URL separately:
var originalString = "http://name.domain.com/image.jpg"
let urlComps = NSURLComponents(string: originalString)!
urlComps.path = "/image_new.jpg"
originalString = urlComps.URL!.absoluteString!
println(originalString) // http://name.domain.com/image_new.jpg
Why not using string interpolation?
var imageName = "image_new"
originalString = "http://images.domain.com/\(imageName).jpg"

Resources