Instruments shows that these lines of code cause memory leaks, what am I doing wrong?
required init(data: JSON) {
self.type = data["type"].stringValue
self.name = data["name"].stringValue
self.numberOfRestaraunts = data["length"].intValue
self.isFavourited = data["isFavourited"].boolValue
self.image = URL(string: data["img"].stringValue)! //<- this
self.id = data["id"].stringValue
self.headerImage = URL(string: data["header"].stringValue)! //<- this
if data["colorSchema"].stringValue == "Dark" {
self.colorTheme = .dark
} else {
self.colorTheme = .light
}
self.color = data["color"].stringValue
self.metaScore = data["metaScore"].intValue
self.typeMetaScore = data["typeMetaScore"].int ?? 0
}
It actually shows, that leaks are NSURL class.
EDIT: Screenshots:
You are force unwrapping the optional objects. Try to change the line self.image = URL(string: data["img"].stringValue)! to
if let url = URL(string: data["img"].stringValue) {
self.image = url
}
and this self.headerImage = URL(string: data["header"].stringValue)! line to
if let url = URL(string: data["header"].stringValue) {
self.headerImage = url
}
Force unwrapping is not a good practice, you should avoid it when possible. Hope this helps!
Could you try this?
if let image_url = URL(string: data["img"].stringValue)
{
self.image = image_url
}
...and this too...
if let header_url = URL(string: data["header"].stringValue)
{
self.headerImage = image_url
}
Could you chech if JSON type subscript returns an Optional?
Related
I am trying to show an image into my table cell view from an API. But it has given a partial link there, as a result, I am getting NSURL connection error code -1002.
Here is my API link: https://api.opendota.com/api/heroStats
I am trying to parse "icon" among them:
"img": "/apps/dota2/images/heroes/antimage_full.png?",
"icon": "/apps/dota2/images/heroes/antimage_icon.png",
My code:
// Generating imageview
if let imageURL = URL(string: heroes[indexPath.row].icon){
print (imageURL)
DispatchQueue.global().async {
let data = try? Data (contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.charIcon.image = image
} //end of 2nd dispatch
}//end of if
}//end of 1st dispatch
}// end of imageURL
How can I solve this problem? Any easy way for swift 4?
You can get the url components of your api link and use your icon "partial link" to set the path property of the URL components. After that you just need to get the resulting url of the url components:
let apiLink = "https://api.opendota.com/api/heroStats"
let apiURL = URL(string: apiLink)!
if var urlComponents = URLComponents(url: apiURL, resolvingAgainstBaseURL: false) {
let iconString = "/apps/dota2/images/heroes/antimage_icon.png"
urlComponents.path = iconString
if let iconURL = urlComponents.url {
print(iconURL.absoluteString)
}
}
This will print
https://api.opendota.com/apps/dota2/images/heroes/antimage_icon.png
You can create a custom method to return a new URL based on the new path string as follow:
extension URL {
var urlComponents: URLComponents? {
return URLComponents(url: self, resolvingAgainstBaseURL: false)
}
func bySettingNew(path: String) -> URL? {
guard var urlComponents = urlComponents else { return nil }
urlComponents.path = path
return urlComponents.url
}
}
let apiLink = "https://api.opendota.com/api/heroStats"
let apiURL = URL(string: apiLink)!
let iconString = "/apps/dota2/images/heroes/antimage_icon.png"
if let iconURL = apiURL.bySettingNew(path: iconString) {
print(iconURL.absoluteString)
}
You can also add this helper to your project to make it easier for you to download an image asynchronously into your image view:
extension UIImageView {
func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.contentMode = mode
self?.image = image
}
}.resume()
}
}
if let imageURL = apiURL.bySettingNew(path: heroes[indexPath.row].icon) {
cell.charIcon.downloaded(from: imageURL)
}
So I have managed to play a video on Chromecast. But only one at a time. I've been trying to figure how to programmatically add to the queue. The idea is to keep playing videos all day. In the code below "playthisvideo()" randomly returns a string that contain an http://.....mp4 . I've look at Google's documentation, it's either too vague or I just don't understand it. And I can't seem to find any examples that would lead the way for me to follow.
func castthevideo() {
let metadata = GCKMediaMetadata()
metadata.setString("Los Simpsons", forKey: kGCKMetadataKeyTitle)
metadata.setString ("Barista: ¿Cómo tomas tu café? " +
" Yo: Muy, muy en serio.",
forKey: kGCKMetadataKeySubtitle)
metadata.addImage(GCKImage(url: URL(string: "https://m.media-amazon.com/images/M/MV5BYjFkMTlkYWUtZWFhNy00M2FmLThiOTYtYTRiYjVlZWYxNmJkXkEyXkFqcGdeQXVyNTAyODkwOQ##._V1_.jpg")!,
width: 480,
height: 360))
let PTV = playthisvideo()
let url = URL.init(string: PTV)
print ("****** ", PTV)
guard let mediaURL = url else {
print("****** invalid mediaURL")
return }
//let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: mediaURL)
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
let mediaInformation = mediaInfoBuilder.build()
if let request = sessionManager.currentSession?.remoteMediaClient?.loadMedia(mediaInformation) { request.delegate = self }
GCKCastContext.sharedInstance().presentDefaultExpandedMediaControls()
}
func castanthor(byAppending appending: Bool) {
let PTV = playthisvideo()
let url = URL.init(string: PTV)
guard let mediaURL = url else {
print("invalid mediaURL")
return
}
myNSNumber = (1 as NSNumber)
if let remoteMediaClient = GCKCastContext.sharedInstance().sessionManager.currentCastSession?.remoteMediaClient {
let builder = GCKMediaQueueItemBuilder()
builder.mediaInformation = selectedItem.mediaInfo
builder.autoplay = true
builder.preloadTime = 3
let item = builder.build
if remoteMediaClient.mediaStatus != nil, appending {
let request = remoteMediaClient.queueInsert(item(), beforeItemWithID: kGCKMediaQueueInvalidItemID)
request.delegate = self
} else {
let options = GCKMediaQueueLoadOptions()
options.repeatMode = remoteMediaClient.mediaStatus?.queueRepeatMode ?? .off
let request = castSession.remoteMediaClient?.queueLoad([item()], with: options)
request?.delegate = self
}
}}
var mediaItems = [GCKMediaQueueItem]()
var urls = // Array of only audio and videos
for index in 0..<urls.count {
let builder = GCKMediaQueueItemBuilder()
let mediaInfoBuilder = GCKMediaInformationBuilder.init(contentURL: urls[i])
mediaInfoBuilder.streamType = GCKMediaStreamType.none;
mediaInfoBuilder.contentType = "video/mp4"
mediaInfoBuilder.metadata = metadata;
let mediaInformation = mediaInfoBuilder.build()
builder.mediaInformation = mediaInformation
builder.autoplay = true
builder.preloadTime = 3
let item = builder.build
mediaItems.append(item)
}
if let remoteMediaClient = GCKCastContext.sharedInstance().sessionManager.currentCastSession?.remoteMediaClient {
let loadOptions = GCKMediaQueueLoadOptions()
loadOptions.repeatMode = .all
loadOptions.startPosition = 0
remoteMediaClient.queueLoadItems(mediaItems, withOptions:loadOptions)
}
I have this url https://storage.googleapis.com/user_avatars/63/img_-qLgH80SBqNhMRYbDQeccg.jpg
I need only qLgH80SBqNhMRYbDQeccg image name from this link In ui Image
You can use NSURL to safely isolate the filename then use substring to get the part you want.
let s = "https://storage.googleapis.com/user_avatars/63/img_-qLgH80SBqNhMRYbDQeccg.jpg"
Swift 2
if let url = NSURL(string: s),
withoutExt = url.URLByDeletingPathExtension,
name = withoutExt.lastPathComponent {
let result = name.substringFromIndex(name.startIndex.advancedBy(5))
print(result)
}
Swift 3
if let url = URL(string: s),
withoutExt = try? url.deletingPathExtension(),
name = withoutExt.lastPathComponent {
let result = name.substring(from: name.index(name.startIndex, offsetBy: 5))
print(result)
}
Swift 4
if let url = URL(string: s) {
let withoutExt = url.deletingPathExtension()
let name = withoutExt.lastPathComponent
let result = name.substring(from: name.index(name.startIndex, offsetBy: 5))
print(result)
}
Prints:
qLgH80SBqNhMRYbDQeccg
What about something that uses NSURLComponents to break up the URL:
func parseURLForFileName(url:String) ->String?
{
let components = NSURLComponents(string: url)
if let path:NSString = components?.path
{
let filename = path.lastPathComponent
if let range = filename.rangeOfString("_-")
{
return filename.substringFromIndex(range.endIndex)
}
}
return nil
}
You would then call it like this:
let name = parseURLForFileName("https://storage.googleapis.com/user_avatars/63/img_-qLgH80SBqNhMRYbDQeccg.jpg")
print(name)
let imgURL:NSURL = NSURL(string: "\(ImageName)")!
at the above line,i'm getting fatal error
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
Code :
let ImageName = obj["image"] as! String
let imgURL:NSURL = NSURL(string: "\(ImageName)")!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let Imgtask = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if (error == nil && data != nil)
{
func display_image()
{
pointAnnoation.DisplayImage = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
Imgtask.resume()
From the above code im trying to store my image from database in annotation
if i printed the 'ImageName' it returns the name from the database correctly, but unable to retain the image
it resulting in the error while running.
You say that
if i printed the 'ImageName' it returns the name from the database correctly
Then that must mean that the ImageName is not valid for a URL
If you look at the description of NSURL(string:) it says:
The URL string with which to initialize the NSURL object. This URL string must conform to URL format as described in RFC 2396, and must not be nil. This method parses URLString according to RFCs 1738 and 1808.
So the question is...how does ImageName look? And can you create a URL from it?
Apart from that, it is always a good idea to use ? instead of ! as #PhillipMills says
Update: I can see that you have posted an example of your URL now. If I do this in a playground:
let url = NSURL(string: " goo.gl/pBmA0d")
I get nil in return, so it would seem that short URLs and NSURLaren't the best of friends.
Update 2: hmm, guess I spoke to quickly, if you look at the above you can see that I have a space before the goo.gl part, if I change that to:
let url = NSURL(string: "goo.gl/pBmA0d")
it actually works, I get a NSURL object.
But another thing I stumbled upon in your code. You declare ImageName as a String here:
let ImageName = obj["image"] as! String
So you don't have to wrap it in \() later on
let imgURL:NSURL = NSURL(string: "\(ImageName)")!
You could simply say:
let imageURL = NSURL(string: ImageName)
And then...as others has said, it is always a good idea to use ? instead of !
So you could write:
if let imageName = obj["image"] as? String,
let imageURL = NSURL(string: imageName) {
//we're in business :-)
}
and be safe and sound
Try to use guard or if let for helping yourself.
let ImageName = obj["image"] as! String
if let imgURL = NSURL(string: ImageName) {
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let Imgtask = session.dataTaskWithRequest(request){ (data, response, error) -> Void in
if (error == nil && data != nil)
{
// What's that func??
func display_image()
{
pointAnnoation.DisplayImage = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
}
Imgtask.resume()
Don't make force unwrap...use if let to avoid crash ...
if let img = obj["image"] as? String,
imgURL = NSURL(string: img) {
// ... continue with your code ...
}
Please try the following code:
//ImageName is a String type.
guard let ImageName = obj["image"] as? String , let imgURL = NSURL(string: ImageName) else{
return
}
let request: NSURLRequest = NSURLRequest(URL:imgURL)
let session = NSURLSession.sharedSession()
let Imgtask = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if (error == nil && data != nil)
{
func display_image()
{
pointAnnoation.DisplayImage = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
Imgtask.resume()
This is my NSURL
let url = NSURL(string: "tel://\(phoneCall)")
It is always nil, although the phoneCall is a valid mobile number string, and I am testing on a real iPhone.
I tried
let url = NSURL(string: "tel:\(phoneCall)")
but it's still nil.
let phoneCall = "tel://9000002143";
let url:NSURL = NSURL(string:phoneCall);
//OR
var url:NSURL = NSURL(string: "tel://9000002143")
if phoneCall == nil || phoneCall.isEmpty
{
print("phoneCall is empty or nil.")
}
else
{
UIApplication.sharedApplication().openURL(url);
}
Where this phoneCall is set?
I had a similar issue with a optional type. This code illustrate the problem:
let user = row.value
let phone = user?.phone
let url = NSURL(string: "tel://\(phone)")
UIApplication.sharedApplication().openURL(url!)
In this case, the value of urlString variable is "tel://Optional(\"11XXXXXXXXX\")", because the user variable can be nil.
You have to ensure that the user is not nil first, something like:
guard let user = row.value else {
return
}
let phone = user.phone
let urlString = "tel://\(phone)"
let url = NSURL(string: urlString)
UIApplication.sharedApplication().openURL(url!)