Clickable activities list Kotlin - list

I'm retrieving data to list and I need to make 2 different intents. "Activities" needs to start "e" intent and rest starts "i" intent.
class MyAdapter(private val context : Activity, private val arrayList : ArrayList<Person>) : ArrayAdapter<Person>(context,
R.layout.list_item, arrayList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val inflater : LayoutInflater = LayoutInflater.from(context)
val view : View = inflater.inflate(R.layout.list_item, null)
val imageView : ImageView = view.findViewById(R.id.profile_pic)
val activities : TextView = view.findViewById(R.id.activity_name)
val person_name : TextView = view.findViewById(R.id.name_list)
val activity_time : TextView = view.findViewById(R.id.time)
val checkbox : ImageView = view.findViewById(R.id.checkbox)
val person_icon : ImageView = view.findViewById(R.id.person_icon)
imageView.setImageResource(arrayList[position].imageID)
activities.text = arrayList[position].activities
person_name.text = arrayList[position].name
activity_time.text = arrayList[position].activityTime
checkbox.setImageResource(arrayList[position].checkbox)
person_icon.setImageResource(arrayList[position].person_icon)
return view
}
}
binding.listview.adapter = MyAdapter(this, personArrayList)
binding.listview.isClickable = true
binding.listview.setOnItemClickListener { parent, view, position, id ->
val name = name[position]
val activities = activities[position]
val activityTime = activityTime[position]
val description = description[position]
val imageID = imageID[position]
val i = Intent(this, DescriptionActivity::class.java)
val e = Intent(this, ProfileScreen::class.java)
i.putExtra("name", name)
i.putExtra("activityTime", activityTime)
e.putExtra("activities", activities)
i.putExtra("imageID", imageID)`your text`
i.putExtra("description", description)
startActivity(i)
}
startActivity (e) overrides startActivity(i)
and making activities OnClickListener crashes the app.

Related

How to keep reference of data when using ObservableObject

I am new to Swiftui and I struggle to understand how to properly retain data created in ObservableObject when rendering views? Or a completely different approach to the problem maybe?
More specifically, it is about getting HTTP data in each row in a List().
Right now, it makes the HTTP call far too often when parent views are rendered, which causes all rows to be reloaded.
The same issue can be found here: Keep reference on view/data model after View update
public class VideoFetcher: ObservableObject {
#Published var video: VideoResponse?
#Published var coverImage: UIImage?
#Published var coverImageLoading = false
#Published var categories: String?
#Published var loading = false
#Published var error = false
func load(mediaItemSlug: String = "", broadcasterSlug: String = "") {
self.loading = true
Video.findBySlug(
mediaItemSlug: mediaItemSlug,
broadcasterSlug: broadcasterSlug,
successCallback: {video -> Void in
self.video = video
self.loading = false
self.setCategories()
self.loadCoverImage()
},
errorCallback: {(error, _) -> Void in
self.loading = false
self.error = true
})
}
func loadCoverImage() {
guard self.video!.coverImageUrl != "" else {
return
}
self.coverImageLoading = true
let downloader = ImageDownloader()
let urlRequest = URLRequest(url: URL(string: self.video!.coverImageUrl)!)
let filter = AspectScaledToFillSizeFilter(size: CGSize(width: 520.0, height: 292.499999963))
downloader.download(urlRequest, filter: filter) { response in
if case .success(let image) = response.result {
self.coverImage = image
self.coverImageLoading = false
}
}
}
func setCategories() {
if (self.video!.broadcaster.categories.count > 0) {
let categoryNames = self.video!.broadcaster.categories.map { category in
return category.name == "" ? "(no name)" : category.name
}
self.categories = categoryNames.joined(separator: " • ");
}
}
}
List() row:
struct VideoCard: View {
#ObservedObject var fetcher = VideoFetcher()
...
init() {
// Causes reload each render
self.fetcher.load()
}
var body: some View {
...
.onAppear {
// Loads that on appear but fetcher.video is nil after view re-rendered because load() wasn't called
self.fetcher.load()
}
}
}
Thanks, Chris. I thought I was doing something wrong on an architectural level but I added caching and that solved my problem.
import Alamofire
import AlamofireImage
import Cache
public class VideoFetcher: ObservableObject {
#Published var video: VideoResponse?
#Published var coverImage: UIImage?
#Published var coverImageLoading = false
#Published var broadcasterImage: UIImage?
#Published var categories: String?
#Published var loading = false
#Published var error = false
func load(mediaItemSlug: String = "", broadcasterSlug: String = "") {
let videoCache = try? AppCache.video!.object(forKey: mediaItemSlug)
if (videoCache != nil) {
self.video = videoCache
self.setCategories()
self.loadCoverImage()
return
}
self.loading = true
Video.findBySlug(
mediaItemSlug: mediaItemSlug,
broadcasterSlug: broadcasterSlug,
successCallback: {video -> Void in
try? AppCache.video!.setObject(video, forKey: mediaItemSlug)
self.video = video
self.loading = false
self.setCategories()
self.loadCoverImage()
self.loadBroadcasterImage()
},
errorCallback: {(error, _) -> Void in
self.loading = false
self.error = true
})
}
func loadCoverImage() {
let coverImageUrl = self.video!.coverImageUrl
guard coverImageUrl != "" else {
return
}
let urlRequest = URLRequest(url: URL(string: coverImageUrl)!)
let cachedImage = AppCache.image!.image(for: urlRequest, withIdentifier: coverImageUrl)
if (cachedImage != nil) {
self.coverImage = cachedImage
return
}
self.coverImageLoading = true
let downloader = ImageDownloader(imageCache: AppCache.image!)
let filter = AspectScaledToFillSizeFilter(size: CGSize(width: 520.0, height: 292.499999963))
downloader.download(urlRequest, filter: filter) { response in
if case .success(let image) = response.result {
AppCache.image!.add(image, for: urlRequest, withIdentifier: coverImageUrl)
self.coverImage = image
self.coverImageLoading = false
}
}
}
func loadBroadcasterImage() {
let broadcasterImage = self.video!.broadcaster.avatarImageUrl
guard broadcasterImage != "" else {
return
}
let urlRequest = URLRequest(url: URL(string: broadcasterImage)!)
let cachedImage = AppCache.image!.image(for: urlRequest, withIdentifier: broadcasterImage)
if (cachedImage != nil) {
self.broadcasterImage = cachedImage
return
}
let downloader = ImageDownloader(imageCache: AppCache.image!)
let filter = AspectScaledToFillSizeFilter(size: CGSize(width: 16, height: 16))
downloader.download(urlRequest, filter: filter) { response in
if case .success(var image) = response.result {
image = image.af.imageRoundedIntoCircle()
AppCache.image!.add(image, for: urlRequest, withIdentifier: broadcasterImage)
self.broadcasterImage = image
}
}
}
func setCategories() {
let categories = self.video!.broadcaster.categories
if (categories.count > 0) {
let categoryNames = categories.map { category in
return category.name == "" ? "(no name)" : category.name
}
self.categories = categoryNames.joined(separator: " • ");
}
}
}

How to publish changes to a single object in a object array

I have the following classes
class ListItem: Identifiable {
var id: UUID
var name: String
var description: String
var isFavorite: Bool
var debugDescription: String {
return "Name: \(self.name) | Favorite?: \(self.isFavorite)"
}
public init(name: String) {
self.name = name
id = UUID()
self.description = "Some text describing why \(self.name.lowercased()) is awesome"
self.isFavorite = false
}
}
class ListItems: ObservableObject {
#Published var items: [ListItem]
let defaultAnimals = ["Ant", "Bear", "Cat", "Dog", "Elephant",
"Fish", "Giraffe", "Hyena", "Iguana", "Jackal", "Kingfisher", "Leopard", "Monkey"]
public init(animals: [String] = []) {
let animalList: [String] = animals.count > 0 ? animals : defaultAnimals
self.items = animalList.sorted {
$0.lowercased() < $1.lowercased()
}.map {
ListItem(name: $0.firstUppercased)
}
}
}
and the following image view in ContentView
struct ContentView: View {
#ObservedObject var list: ListItems = ListItems()
var body: some View {
List(list.items) {
animal in HStack {
// ...
Image(systemName: animal.isFavorite ? "heart.fill" : "heart").foregroundColor(.pink).onTapGesture {
let index = self.list.items.firstIndex { $0.id == animal.id } ?? -1
if (index >= 0) {
self.list.items[index].isFavorite = !animal.isFavorite
self.list.items = Array(self.list.items[0...self.list.items.count-1]) // <--
}
}
// ...
}
}
}
}
Everytime, the image view is tapped, I am basically reassigning the entire array like this so that the changes can be reflected in the UI
self.list.items = Array(self.list.items[0...self.list.items.count-1])
My question: How can I refactor my code to prevent reassigning the entire object array every time some object property changes?
I am fairly new to Swift & iOS development, not sure if I am missing something basic.
Declare ListItem as an struct instead of a class, this way the view will be notified when isFavorite changes. And just a little suggestion; you can use toggle to change the value of a boolean: self.list.items[index].isFavorite.toggle()

how to display multiple contact of a single user swift 3

I am fetching user's information like his name,phone number and email id from contacts.But it is only showing first contact number.IF a person has more than one contact number,it didnt show that second number.Can someone help?I am using this function
where EVContactProtocol is part of Library
func didChooseContacts(_ contacts: [EVContactProtocol]?) {
var conlist : String = ""
if let cons = contacts {
for con in cons {
if let fullname = con.fullname(),let email1 = con.email , let phoneNumber = con.phone {
conlist += fullname + "\n"
print("Full Name: ",fullname)
print("Email: ",email1)
print("Phone Number: ",phoneNumber)
}
}
self.textView?.text = conlist
} else {
print("I got nothing")
}
let _ = self.navigationController?.popViewController(animated: true)
}
You should try this:
import Contacts
class ViewController: UIViewController
{
lazy var contacts: [CNContact] =
{
let contactStore = CNContactStore()
let keysToFetch = [
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
CNContactEmailAddressesKey,
CNContactPhoneNumbersKey] as [Any]
// Get all the containers
var allContainers: [CNContainer] = []
do
{
allContainers = try contactStore.containers(matching: nil)
}
catch
{
print("Error fetching containers")
}
var results: [CNContact] = []
// Iterate all containers and append their contacts to our results array
for container in allContainers
{
let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier)
do
{
let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keysToFetch as! [CNKeyDescriptor])
results.append(contentsOf: containerResults)
}
catch
{
print("Error fetching results for container")
}
}
return results
}()
override func viewDidLoad()
{
super.viewDidLoad()
print(contacts[0].givenName)
print(contacts[0].phoneNumbers)
print(contacts[0].emailAddresses)
print(contacts)
}
}

How to initialize class with optional properties in Swift 3

I get a web API response, which I loop through and initialize a Customer class for each JSON object in the array. The Customer class has a base class with a couple optional properties. I get errors when there are null values in the JSON, and I am not sure how to handle them properly. I am especially unsure of how to handle a null Date in swift. If anyone could give me some advice, I would really appreciate it!
JSON returned from Web API:
[{"Id":1,"BusinessId":1,"CompanyName":"Test Company4","FirstName":"Mike","LastName":"Doe","CustomerType":1,"CustomerStatus":1,"IsDeleted":false,"Created":"2016-12-22T20:12:10.2760144Z","CreatedBy":1,"Modified":"2016-12-29T19:29:26.1245219Z","ModifiedBy":1},{"Id":2,"BusinessId":1,"CompanyName":"Test Company5","FirstName":"Mike","LastName":"Doe","CustomerType":1,"CustomerStatus":1,"IsDeleted":false,"Created":"2017-01-03T20:18:51.7639708Z","CreatedBy":1,"Modified":null,"ModifiedBy":null}]
Customer class:
class Customer : BaseEntity {
var Id: Int64
var BusinessId: Int64
var CustomerType: Int64
var CustomerStatus: Int64
var CompanyName: String
var FirstName: String
var LastName: String
init?(json: [String: Any]) {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
guard let id = json["Id"] as? Int64,
let businessId = json["BusinessId"] as? Int64,
let customerType = json["CustomerType"] as? Int64,
let customerStatus = json["CustomerStatus"] as? Int64,
let companyName = json["CompanyName"] as? String,
let firstName = json["FirstName"] as? String,
let lastName = json["LastName"] as? String,
let isDeleted = json["IsDeleted"] as? Bool
else {
return nil
}
var created = Date()
if let cdate = json["Created"] as? String {
created = formatter.date(from: cdate)!
}
let createdBy = json["CreatedBy"] as? String
var modified = Date()
if let mdate = json["Modified"] as? String {
modified = formatter.date(from: mdate)!
}
let modifiedBy = json["ModifiedBy"] as? String
self.Id = id
self.BusinessId = businessId
self.CustomerType = customerType
self.CustomerStatus = customerStatus
self.CompanyName = companyName
self.FirstName = firstName
self.LastName = lastName
super.init(isDeleted: isDeleted, created: created, createdBy: createdBy, modified: modified, modifiedBy: modifiedBy)
}
}
BaseEntity class that contains some optional properties:
class BaseEntity {
var IsDeleted: Bool
var Created: Date
var CreatedBy: String
var Modified: Date?
var ModifiedBy: String?
init(isDeleted: Bool, created: Date, createdBy: String, modified: Date, modifiedBy: String) {
self.IsDeleted = isDeleted
self.Created = created
self.CreatedBy = createdBy
self.Modified = modified
self.ModifiedBy = modifiedBy
}
}
One of the problems I am running into is when I initialize Customer, my Customer object looks like this. Then when I try to insert it into a database, it does not recognize the BaseEntity properties as properties of Customer:
Id = 1
BusinessId = 1
CustomerType = 1
CustomerStatus = 1
CompanyName = "Test Company4"
FirstName = "Mike"
LastName = "Doe"
BaseEntity
IsDeleted = true
Created = '1/1/2017'
CreatedBy = 1
Modified = '1/1/2017'
ModifiedBy = 1
You need to define your class type for model
class BaseEntity {
to
class BaseEntity: NSObject {
This is the way you can create your model class: -
//Model Class
class BaseModel: NSObject {
var name: String
var address: String
var mobilenumber: Int
init(name: String?, address: String?, mobilenumber: Int?) {
self.name = name ?? ""
self.address = address ?? ""
self.mobilenumber = mobilenumber ?? 0
}
}
//Pass value inside model class
class ViewController: UIViewController {
var model = [BaseModel]() //Model Array Initialization here
override func viewDidLoad() {
super.viewDidLoad()
//Pass value inside Model
model.append(BaseModel(name: "Your name", address: "Your address", mobilenumber: 5545545452))
}
}
//Get value from Model class
class DetailsController: UIViewController {
var details: BaseModel?
override func viewDidLoad() {
super.viewDidLoad()
//Retrive value from model
let name = details?.name ?? ""
let address = details?.address ?? ""
let mobile = details?.mobilenumber ?? 0
}
}

swift 3, PHFetchResult.enumerateObjects error

In swift 3,the method is show me "ambiguous use of 'enumerateObjects'",what happen.how can i do?
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects { (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
}
return list
}
}
Swift 3.0: Just add the Round Brackets before Curly Brackets starts after enumerateObjects.
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects ({ (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
})
return list
}
}
Do something like this noh. You can't directly add extension for PHFetchResult because it has other ObjectType as its generic parameter PHFetchResult<ObjectType> . So you must do something else.
class FetchPhoto {
class func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
PHAssetCollection.fetchMoments(with: nil).enumerateObjects(EnumerationOptions.concurrent) { (collection, _, _) in
list.append(collection)
}
return list
}
}
PHAssetCollection.fetchMoments returns PHFetchResult<PHAssetCollection> here PHAssetCollection is the ObjectType for the PHFetchResult. You got the ambiguous error because you have not specified the objectType.
A generic way to approach this.
class FetchPhoto {
class func assetCollection<T : PHObject>(result : PHFetchResult<T>) -> [T] {
var list : [T] = []
result.enumerateObjects(EnumerationOptions.concurrent) { (object, _, _) in
list.append(object)
}
return list
}
}
Swift 3
class PhotosHelper {
class func fetchAllLocalIdentifiersOfPhotos(completion : (_ localIdentifiers : [String]) -> ()) {
let photos : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
photos.enumerateObjects ({ _,_,_ in
// Do your operations, you can see that there is no warnings/errors in this one
})
}
}